home *** CD-ROM | disk | FTP | other *** search
/ Aminet 52 / Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso / Aminet / docs / mags / saku11.lha / Teksti / TechnoBBS.txt < prev    next >
Text File  |  1995-02-28  |  72KB  |  1,612 lines

  1. 5
  2. 1*
  3.  
  4. {3                          TechnoBBS - Osa 3: Menukieli
  5. {3                          ----------------------------
  6.  
  7.                                   Sami Klemola
  8.  
  9.  
  10. Artikkelin tässä osassa käsittelen TechMenun ja sen käyttämän kielen.  Kieli  on
  11. C:n tyylinen, mutta paljon yksinkertaisempi. Ensiksi kuvailen kielen  rakenteen,
  12. sitten siirrytään ohjelmointiin ja tehdään muutama komento. Sen jälkeen  esitte-
  13. len lisää TechnoBBS:n funktioita ja komentoja. Lopuksi tulee valmiita uusia  ko-
  14. mentoja menukoodilistauksina. Tämä on  näillä  näkymin  viimeinen  TechnoBBS:ään
  15. liittyvä artikkeli. Jatkossa käsiteltäviä aiheita kuitenkin voisivat olla  Tech-
  16. Mail ja TechnoBBS:n liittäminen verkkoon. 
  17.  
  18.  
  19. {3Ohjelmat ja niiden ajaminen
  20. {3---------------------------
  21.  
  22. TechMenu on menukooditulkki, joka ajaa tokenisoitua menukoodia.  ASCII-muotoinen
  23. lähdekoodi on ensin käännettävä ajettavaksi koodiksi. Siihen tarkoitukseen  kuu-
  24. luu ohjelmistoon TechMenuComp:
  25.  
  26. TechMenuComp <source> <destination>
  27.  
  28. Lähdekoodin oikeellisuuden kanssa kannattaa olla tarkkana, koska  kun  TechMenu-
  29. Comp sekoaa, se valittaa aivan jokaisesta merkistä, joka  tiedostossa  on.  Vir-
  30. heilmoituksia tulee satoja... Lisäksi virhetilanteessa ohjelma aiheuttaa laitto-
  31. mia muistiosoituksia, Enforcer-hittejä ja jumiutuu joillakin tietyillä virheillä
  32. ja kaatuileekin! Virheettömän koodin kääntämisessä ei ole onglemia.
  33.  
  34. ASCII-tiedoston pääte on dat ja käännetyn koodin menu. Esimerkiksi Doors-valikko
  35. kääntyy näin:
  36.  
  37. TechMenuComp Doors.dat Doors.menu
  38.  
  39. TechMenuComp kääntää Doors.dat:n ja  tuottaa  sen  pohjalta  Doors.menun,  jonka
  40. TechMenu lataa ja suorittaa. TechMenulle annetaan myös noden numero:
  41.  
  42. TechMenu <node> <menu>
  43.  
  44. Silloin käynnistetään päävalikko tai jokin muu, se valikko, johon käyttäjän  ha-
  45. lutaan tulevan sisään:
  46.  
  47. TechMenu 1 Main.menu
  48.  
  49. Valmis menukoodi on nimeltään MainMenu.menu. Minä käytän kuitenkin vain  valikon
  50. nimeä, koska "Menu" olisi nimessä aivan turha ja epälooginen lisä. Login-koodis-
  51. sa suoritettava käsky tuli jo edellisessä osassa, mutta laitan sen  tähän  vielä
  52. uudestaan. Heti login-ohjelmien ajamisen jälkeen suoritetaan:
  53.  
  54. address command "TechMenu "||ln||" BBS:Menu/Main.menu"
  55.  
  56. TechMenun ajo keskeytetään esimerkiksi,  kun  käyttäjä  antaa  Goodbye-komennon.
  57. Silloin suoritus palaa Rexx-ohjelmaan, ja siinä tulisi seuraavana olla tarvitta-
  58. va Goodbye-koodi. Täydellinen login-ohjelma julkaistiin edellisessä osassa. 
  59.  
  60.  
  61.  
  62.  
  63.  
  64. {3Lähdetiedosto
  65. {3-------------
  66.  
  67. TechMenuCompille annettava lähdekoodi on tavallista ASCII-tekstiä.  Siihen  voi-
  68. daan sisällyttää kommentteja tavalliseen C-tyyliin.  Kommentti  alkaa  merkeillä
  69. "/*" ja päättyy merkkeihin "*/". Kieli on muutenkin hyvin lähellä  C:tä.  Kielen
  70. operaattorit ovat samantapaisia, lauseet päätetään ";"-merkkiin ja  ryhmät  koo-
  71. taan kaarisulkeisiin. TechMenu tuntee kahdeksan toimintoa: 
  72.  
  73.          Command           Määrittelee komennon
  74.          Execute           Suorittaa toimintoja
  75.          MenuName          Asettaa valikon nimen
  76.          MenuText          Määrittelee valikkotekstitiedoston nimen
  77.          Prompt            Määrittelee kehoitteen
  78.          HotOn             Asettaa ohjelman hotkey-tilaan tai sitten ei
  79.          LoadMenu          Lataa toisen valikon
  80.          Unknown           Määrittelee virhemerkkijonon
  81.  
  82. Näistä merkittävin on Command. Se määrittelee purkkiin komennon.  Useita  näistä
  83. ei tarvita kuin kerran tai ei ollenkaan yhdessä menukooditiedostossa.  Käyn  nyt
  84. nämä kaikki läpi yksitellen. 
  85.  
  86. Formaatti   MenuName <string>
  87. Esimerkki   MenuName "Extras"
  88.  
  89. MenuName asettaa valikon nimen. Nykyisellään sitä ei käytetä muuhun kuin valikon
  90. nimen näyttämiseen TechCon-ikkunassa, joten sillä ei ole juuri käyttöä. 
  91.  
  92. Formaatti   MenuText <filename>
  93. Esimerkki   MenuText "Extras"
  94.  
  95. MenuText määrittelee sen tiedoston nimen, jonka  TechnoBBS  lähettää  komennolla
  96. SendMenu. Päätettä nimeen ei tarvitse laittaa, vaan TechnoBBS kokeilee eri vaih-
  97. toehtoja ja katsoo käyttäjän Extensions-määrittelyn. TechnoBBS:n mukana tulevis-
  98. sa valikoissa tätä toimintoa ei hyödynnetä. 
  99.  
  100. Formaatti   Prompt <expression>
  101. Esimerkki   Prompt "\(27)[35m<\(GetTimeLeft())> \(27)[32mExtras: \(27)[0m"
  102.  
  103. Prompt määrittelee kehotteen, jonka TechnoBBS lähettää käyttäjälle aina, kun  se
  104. on valmis vastaanottamaan komennon. Se voi olla yksinkertainen  merkkijono,  tai
  105. kuten yllä, sisältää funktiokutsuja. Yllä määritelty kehote näyttäisi  kutakuin-
  106. kin tältä:
  107.  
  108. <30> Extras:
  109.  
  110. Siinä 30 kuvaa jäljellä olevaa aikaa minuutteina. Nuo kaksi  osaa  tulostuisivat
  111. vielä eri väreillä. Määrittelyn ymmärtäminen ei  ole  tärkeää  tässä  vaiheessa.
  112. Myöhemmin tutustutaan tarkemmin kielen koukeroihin. Erikoistapaus  on  viestiva-
  113. likko, jossa promptissa kannattaa kertoa lisätietoa. Minulla näytetään viestiva-
  114. likon promptissa nykyisen viestialueen nimi ja olemassa olevien  viestien  rajat
  115. eli alueen ensimmäisen ja viimeisen viestin numerot. 
  116.  
  117. Formaatti   Unknown <expression>
  118. Esimerkki   Unknown "\(27)[31mKomentosi \"%s\" on minulle vieras.\(27)[0m"
  119.  
  120. Unknown määrittelee merkkijonon, joka lähetetään käyttäjälle, kun hän on antanut
  121. tuntemattoman komennon. Komento on tuntematon, kun sitä ei ole määritelty nykyi-
  122. sessä menukoodissa tai missä tahansa ladatussa menukoodissa globaalina  komento-
  123. na. Unknown hyväksyy samanlaisen määrittelyn kuin Prompt. Merkkijonossa oleva %s
  124. korvataan komennon nimellä, ja tässä se tulostuu lainausmerkkien  sisään,  jotka
  125. on escapoitava slashilla, jotta tulkki ei luulisi quoten olevan merkkijonon ter-
  126. minaattori. 
  127.  
  128. Formaatti   HotOn <misc>
  129. Esimerkki   HotOn "HOTKEYS"
  130.  
  131. HotOn määrittelee UserMisc-muuttujan, jonka arvo määrää,  mennäänkö  Hotkey-moo-
  132. diin vai ei. Jos määritellyn muuttujan arvo on nolla, toimitaan, kuin HotOn-toi-
  133. mintoa ei olisi määriteltykään. Muussa  tapauksessa  siirrytään  Hotkey-moodiin,
  134. jossa  komentojen  nimien  kirjoittamisen  sijaan  ne   valitaan   yksittäisillä
  135. näppäimenpainalluksilla. Tällöin luonnollisesti kaikkien  käytettäväksi  tarkoi-
  136. tettujen komentojen on erottava ensimmäisestä merkistään, joka on Hotkey-moodis-
  137. sa se koodi, joka laukaisee komennon. 
  138.  
  139. Formaatti   LoadMenu <menu>
  140. Esimerkki   LoadMenu "Extras.menu"
  141.  
  142. LoadMenu määrittelee ladattavan menukoodin. Normaalisti  TechMenu  ajetaan  vain
  143. kerran, jolloin sen käsketään ladata päävalikko. Päävalikossa on erinäinen määrä
  144. LoadMenu-toimintoja, jotka lataavat kaikki muut valikot. Tällöin voidaan helpos-
  145. ti siirtyä valikosta  toiseen  menu()-funktiolla.  TechMenun  sisäiset  funktiot
  146. käsitellään yksityiskohtaisesti myöhemmin. Kussakin valikossa voi olla myös glo-
  147. baaleja komentoja, joiden ajaminen onnistuu mistä tahansa valikosta, kun  kaikki
  148. valikot on ladattu. 
  149.  
  150. Formaatti   Execute { <actions> }
  151.  
  152. Execute suorittaa kaarisulkuihin sisällytetyt toimenpiteet. Tämä toiminto ei ole
  153. ollenkaan käyttökelpoinen, koska sen  ollessa  käytössä  kaikki  muut  toiminnot
  154. jätetään huomiotta. Executea käytettäessä tuloksena ei olekaan valikko vaan  sk-
  155. ripti, joten toiminnossa ei ole mitään järkeä. Parempi vaihtoehto  olisi  ollut,
  156. että nämä toimenpiteet suoritetaan valikkoon siirryttäessä ja sen jälkeen  aloi-
  157. tetaan normaali toiminta, mutta sen sijaan  koodin  suoritus  päättyy.  Järjetön
  158. toiminto! 
  159.  
  160. Formaatti   Command "<id>" [<options>] { <actions> } ["<name>"];
  161.  
  162. Command on TechMenun tärkein toiminto. Sillä määritellään purkkiin komento.  Ko-
  163. mennon nimi on ensimmäisessä merkkijonossa, ja se on merkitty yllä id:ksi. Tämän
  164. jälkeen tulevat optiot, jotka määräävät, kenellä  on  oikeus  käyttää  komentoa.
  165. Seuraavaksi tulevat komennossa suoritettavat toimenpiteet kaarisuluissa eli var-
  166. sinainen komennon koodi. Toimenpiteet käydään läpi seuraavassa luvussa.  Viimei-
  167. senä voidaan vielä määritellä komennolle nimi, joka lähetetään käyttäjälle  Hot-
  168. key-moodissa, kun se on tunnistettu.
  169.  
  170. Command-toiminnon optiot voivat olla standalone-optioita tai niihin voi  liittyä
  171. parametri. Optioilla rajoitetaan komennon käyttöä: 
  172.  
  173.         A:<value>         Asettaa minimioikeustason komennon käyttäjälle
  174.         G:                Määrittelee komennon globaaliksi
  175.         M:<value>         Asettaa maskin komennon käyttäjälle
  176.         N:<value>         Määrittelee tarpeellisten merkkien määrän
  177.         O:<expression>    Tarjoaa mahdollisuuden monimutkaiseen kontrolliin
  178.  
  179. Optiot A ja M asettavat käyttäjältä  vaadittavat  oikeusmäärittelyt.  Voidakseen
  180. käyttää komentoa käyttäjän oikeustason tulee olla vähintään yhtä suuri  kuin  A-
  181. optiossa annettu luku. Lisäksi M-optiossa annetun luvun päällä  olevien  bittien
  182. tulee olla päällä myös käyttäjän maskissa.  Kolmas  käyttörajoituksiin  liittyvä
  183. optio on O. Sitä seuraa kokonainen lause, jonka pitää olla tosi (arvo  muu  kuin
  184. nolla), jotta komentoa voi käyttää.
  185.  
  186. Optio G määrittelee komennon globaaliksi.  Globaali  komento  voidaan  suorittaa
  187. missä tahansa valikossa. Optio N määrittelee, kuinka monta merkkiä komennon  ni-
  188. mestä tarvitsee täsmätä, että katsotaan komento annetuksi. Tämä  luku  kannattaa
  189. laittaa aika pieneksi, jotta komennot voidaan lyhentää reilusti. Lyhyillä komen-
  190. noilla optiota ei kannata käyttää ollenkaan, vaan komennot kannattaa  edellyttää
  191. kokonaisina. 
  192.  
  193.  
  194. {3Toimenpiteet
  195. {3------------
  196.  
  197. Komennossa suoritettavat toimenpiteet ovat lähinnä muuttujan asettaminen, suori-
  198. tuksen ohjaus tai funktiokutsu. Muuttujat menukielessä alkavat aina  dollarimer-
  199. killä. Suorituksen ohjaukseen ovat olemassa  funktiontyyliset  if()  ja  while()
  200. sekä quit ja break. Funktiokutsu voi olla TechMenun sisäinen tai ulkoinen  Tech-
  201. Con:n tai TechnoBBS:n funktio. TechnoBBS:n funktioita menukoodista  kutsuttaessa
  202. tulee huomata, ettei noden numeroa tarvita. TechMenu hoitaa sen automaattisesti.
  203.  
  204. Muuttuja asetetaan tutusti:
  205.  
  206. $<id> = <expression>
  207.  
  208. Tässä id on muuttujan nimi ja expression lause, joka tulkitaan ja jonka evaluaa-
  209. tion tulos asetetaan muuttujaan. Lauseiden rakenne käydään läpi  yksityiskohtai-
  210. sesti myöhemmin. Koodin suoritusta ohjaavista toimenpiteistä  if()  on  useimmin
  211. käytetty ja hyödyllisin. Sen avulla voidaan koodin suoritusta ehdollistaa: 
  212.  
  213. if(<expression>) <action>
  214.  
  215. tai
  216.  
  217. if(<expression>) {
  218.     <actions>
  219. }
  220.  
  221. Ylempää tapaa voi käyttää silloin, kun ehdollistettavia  toimenpiteitä  on  vain
  222. yksi. Kun niitä on useampia,  ne  erotetaan  ryhmäksi  kaarisuluilla.  Myös  yk-
  223. sittäinen toimenpide voidaan laittaa kaarisulkeiden sisään. Ehdollistettu koodi,
  224. if():n perässä olevat toimenpiteet, suoritetaan silloin,  kun  if():lle  annettu
  225. lause on tosi. Jos on tarpeen suorittaa tietyt toimenpiteet, kun se ei ole tosi,
  226. lauseen voi invertoida. Lisäksi on mahdollista käyttää else-rakennetta: 
  227.  
  228. else <action>
  229.  
  230. tai
  231.  
  232. else {
  233.     <actions>
  234. }
  235.  
  236. Tällöin else-rakenne tulee heti if():n perään, ja  sen  sisältämät  toimenpiteet
  237. suoritetaan silloin, kun lause ei ole tosi. If() ja else ovat  erilliset  raken-
  238. teelliset osat, eikä kaarisulkeita tarvitse käyttää kummassakin, vaikka toisessa
  239. ne olisivatkin. Toinen koodin suoritusta ohjaava toimenpide on while(): 
  240.  
  241. while(<expression>) <action>
  242.  
  243. tai
  244.  
  245. while(<expression>) {
  246.     <actions>
  247. }
  248.  
  249.  
  250. While() suorittaa perässä tulevia toimenpiteitä niin kauan  kuin  sille  annettu
  251. lause on tosi. Jos se ei ole tosi ensimmäisellä while():n  suorituskerralla,  ei
  252. perässä tulevia toimenpiteitä suoriteta ollenkaan. While()-looppi  voidaan  kes-
  253. keyttää break:lla. Mikäli break ei ole while()-toimenpidesarjassa, se keskeyttää
  254. koko komennon suorituksen. On mahdollista tehdä looppi, josta ei poistuta ollen-
  255. kaan, paitsi break:n avulla: 
  256.  
  257. while(1) {
  258.     /* suoritettavat toimenpiteet */
  259. }
  260.  
  261. Neljäs koodin suoritusta ohjaava toimenpide on quit. Se keskeyttää  suorituksen,
  262. ja quit:n kohdatessaan TechMenu palaa sen ajaneeseen ohjelmaan. 
  263.  
  264.  
  265. {3Sisäiset funktiot
  266. {3-----------------
  267.  
  268. TechMenu tarjoaa neljä funktiota, jotka eivät palauta arvoa,  vaan  ne  toimivat
  269. enemmänkin komentoina ja suoritetaan toimintoina: 
  270.  
  271.          menu()            Vaihtaa valikkoa
  272.          dos()             Suorittaa DOS-komennon
  273.          rexx()            Suorittaa Rexx-komennon
  274.          rxport()          Vaihtaa Rexx-porttia
  275.  
  276. Jo aikaisemmin tuli ilmi mahdollisuus ladata useita valikoita yhtaikaa. Kun  va-
  277. likko on LoadMenu-toiminnolla ladattu, voidaan siihen vaihtaa funktiolla menu().
  278. Parametriksi funktiolle annetaan käännetyn menukoodin tiedostonimi  ilman  hake-
  279. mistopolkua. 
  280.  
  281. Esimerkki: menu("Extras.menu");
  282.  
  283. Funktio dos() suorittaa DOS-komennon. Yleensä sille annetaan suoraan merkkijono,
  284. mutta sille voidaan antaa myös funktiokutsu tai niitä voi olla  osana  merkkijo-
  285. noa. Parametrinä annetun lauseen evaluaation tulos ajetaan DOS-komentona. 
  286.  
  287. Esimerkki: dos("MakeFList");
  288.  
  289. Rexx-ohjelmalle annetaan yleensä parametrinä,  kuten  tunnettua,  noden  numero,
  290. jonka avulla ne kommunikoivat suoraan TechnoBBS-prosessin kanssa.  Ulkoinen  oh-
  291. jelma ei kuitenkaan välttämättä osaa käyttää  TechnoBBS:n  Rexx-liittymää,  vaan
  292. kommunikoi standardien I/O-väylien kautta. Avuksi  tulee  TechIO,  jonka  avulla
  293. saadaan ohjelman stdin- ja stdout-kanavat ohjattua modeemille. Esimerkiksi  tie-
  294. dostolistaus lähetettäisiin näin: 
  295.  
  296. dos("List BBS:Menu <>TECHIO:\(node())");
  297.  
  298. TechIO:n täytyy tietää, minkä noden kanssa ohjelman  tulee  kommunikoida.  Tieto
  299. saadaan sisäisellä funktiolla node(). Näin mitä tahansa ohjelmaa voidaan helpos-
  300. ti käyttää linjalta, kunhan se ei avaa omia ikkunoita tai pyytimiä.
  301.  
  302. Funktio rexx() suorittaa Rexx-komennon. Se toimii samaan tapaan kuin dos().  Ko-
  303. mento lähetetään aktiiviseen Rexx-porttiin, jonka  voi  vaihtaa  funktiolla  rx-
  304. port(). Se vastaa toiminnaltaan melko lailla Rexx-kielen Address-komentoa. 
  305.  
  306. Esimerkki: rexx("SendModem Hello, world!");
  307. Esimerkki: rxport("TECHCON");
  308.  
  309. Yleensä Rexx-portti, johon menukoodista lähetetyt komennot menevät, on  kyseisen
  310. noden TechnoBBS-prosessin portti. Näin esimerkiksi yllä oleva  SendModem-komento
  311. menisi oikeaan osoitteeseen ja toimisi samoin kuin Rexx-koodista. Kaikille funk-
  312. tioille annettavat parametrit ovat lauseita, ja funktiolle varsinaisesti  annet-
  313. tavat argumentit ovat niiden evaluaation tuloksia. 
  314.  
  315. TechMenun varsinaiset sisäiset funktiot ovat:
  316.  
  317.          arg()             Palauttaa komennon argumentteja
  318.          node()            Palauttaa noden numeron
  319.          str()             Ottaa osan merkkijonosta
  320.          len()             Palauttaa merkkijonon pituuden
  321.          upper()           Muuttaa merkkijonon isokirjaimiseksi
  322.          lower()           Muuttaa merkkijonon pienikirjaimiseksi
  323.          split()           Etsii merkkijonosta toista merkkijonoa
  324.  
  325. Komennon nimen saa selville kutsumalla arg(0):aa. Muut arvot palauttavat  varsi-
  326. naisia argumentteja, yksi ensimmäisen, kaksi toisen jne. TechnoBBS ei osaa käsi-
  327. tellä lainausmerkkejä  komentorivillä,  joten  monisanaiset  argumentit  tulevat
  328. erillisinä. Esimerkiksi komennolle parametrinä annettava  käyttäjän  nimi  tulee
  329. aina vähintään kahtena argumenttina. Näiden kanssa tulee olla tarkkana. Kuten jo
  330. tulikin ilmi, node():lla saa selville, millä nodella koodia ajetaan.
  331.  
  332. Funktio str() vastaa BASICin MID$()-funktiota.  Esimerkiksi  str("tapuli",  2,3)
  333. antaisi ulos merkkijonon "apu". Toinen argumentti siis kertoo, mistä kohtaa ale-
  334. taan ottaa merkkijonoa. Arvo 1 tarkoittaa merkkijonon ensimmäistä merkkiä.  Vii-
  335. meinen parametri kertoo, kuinka monta  merkkiä  merkkijonosta  otetaan.  Funktio
  336. len() palauttaa merkkijonon pituuden. Upper() ja lower() muuttavat  kaikki  kir-
  337. jaimet isoiksi ja pieniksi. Funktio split() palauttaa kohdan,  jossa  merkkijono
  338. alkaa toisessa:
  339.  
  340. split("hirsikasa","sika")
  341.  
  342. Tämä lause saisi arvon 4. Merkkijono "sika" löytyy  ensimmäisestä  merkkijonosta
  343. alkaen merkistä 4. Jälleen arvo 1 tarkoittaa merkkijonon ensimmäistä  kirjainta.
  344. Itse asiassa näiden lauseiden ei tarvitse olla merkkijonoja. Periaatteessa Tech-
  345. Menu käsittelee kaikkia lauseita merkkijonoina, ja siksi sen toiminta on joissa-
  346. kin tilanteissa vähintään omituista. 
  347.  
  348.  
  349. {3Lauseet
  350. {3-------
  351.  
  352. Lauseet koostuvat kahdentyyppisistä elementeistä, operaattoreista  ja  arvoista.
  353. Operaattorit ovat samantyylisiä kuin C:ssä ja niitä on kasapäin. Ne  käsitellään
  354. yksitellen myöhemmin. Lauseessa voi olla yksi tai useampi elementti.  Arvoja  on
  355. oltava vähintään yksi, mutta niitä voi olla useita, jolloin ne erotetaan toisis-
  356. taan operaattoreilla, jotka suorittavat niiden välillä jonkin operaation. Lopul-
  357. ta koko lauseen arvo saadaan selville, kun kaikki yksittäiset arvot on selvitet-
  358. ty ja operaatiot ratkaistu. Arvot voivat olla seuraavia: 
  359.  
  360.          $<id>             muuttuja
  361.          "<string>"        merkkijono
  362.          <number>          numero
  363.          <name>([<args>])  funktiokutsu
  364.  
  365. Muuttujan nimi, esimerkiksi $muuttuja, tuo sen kohdalle lauseessa muuttujan  ar-
  366. von. Merkkijono ja numero ovat kiinteitä arvoja, jotka ovat samat joka kerta kun
  367. lausetta ratkaistaan. Funktiokutsu tuo sen kohdalle lauseeseen funktion  palaut-
  368. taman arvon. Lisäksi lauseessa voi olla kolmella tavalla lisää lauseita: 
  369.  
  370.          ~<expression>     käänteistys
  371.          !<expression>     looginen käänteistys
  372.          (<expression>)    sulkeistus
  373.  
  374. Normaalisti suoraan, muuttujasta tai funktiolta saatu arvo tulee lauseeseen sel-
  375. laisenaan, mutta laittamalla "~"- tai "!"-merkki lauseen eteen sen arvo  voidaan
  376. invertoida. Sulkeiden avulla lauseiden suoritusjärjestystä voidaan muuttaa. Nor-
  377. maalisti lauseet tulkitaan vasemmalta oikealle, eikä laskusäännöistä ole tietoa-
  378. kaan. Lause ei siis oikeastaan olekaan vain yksi  lause,  vaan  siinä  voi  olla
  379. vaikka kuinka monta lausetta, jotka on kaikki ratkaistava, ennen kuin  sen  arvo
  380. on selvillä. 
  381.  
  382. Menukoodissa hyväksyttävät operaattorit ovat:
  383.  
  384. ==       yhtäsuuruus: arvo on 1, jos operaattorin kahta puolta olevat
  385.          arvot ovat samat
  386.  
  387. !=       erisuuruus: arvo on 0, jos ne ovat samat, muuten 1
  388.  
  389. >        suurempi kuin: arvo on 1, jos arvo vasemmalla puolella on suurempi
  390.  
  391. <        pienempi kuin: arvo on 1, jos arvo vasemmalla puolella on pienempi
  392.  
  393. >=       suurempi tai yhtä suuri kuin: arvo on 1, jos arvo vasemmalla
  394.          puolella on suurempi tai sama kuin oikealla puolella
  395.  
  396. <=       pienempi tai yhtä suuri kuin: arvo on 1, jos arvo vasemmalla
  397.          puolella on pienempi tai sama kuin oikealla puolella
  398.  
  399. +        yhteenlasku: arvo on operaattorin kahta puolta olevien arvojen
  400.          summa
  401.  
  402. -        vähennyslasku: arvo on vasemmalla puolella oleva arvo vähennettynä
  403.          oikealla puolella olevalla arvolla
  404.  
  405. *        kertolasku: arvo on operaattorin kahta puolta olevien arvojen
  406.          tulo
  407.  
  408. /        jakolasku: arvo on vasemmalla puolella oleva arvo jaettuna
  409.          oikealla puolella olevalla luvulla
  410.  
  411. ++       merkkijonoyhteenlasku: yhdistää operaattorin kahta puolta olevat
  412.          merkkijonot
  413.  
  414. |        tai-toiminto operaattorin kahta puolta olevilla arvoilla
  415.  
  416. ||       looginen tai-toiminto
  417.  
  418. &        ja-toiminto
  419.  
  420. &&       looginen ja-toiminto
  421.  
  422. ^        ehdoton tai -toiminto
  423.  
  424. Merkkijonoa on syytä vielä käsitellä tarkemmin. Siinä käytetään kenoviivaa esca-
  425. pe-merkkinä. Esimerkiksi lainausmerkki saadaan merkkijonoon,  kun  sitä  edeltää
  426. kenoviiva. Muuten se tulkitaan merkkijonon päätösmerkiksi. Näin  myös  kenoviiva
  427. itse on escapoitava. Kenoviivaa voivat seurata sulkeet, jolloin merkkijonoon tu-
  428. lee sulkeissa olevaa arvoa vastaava ASCII-merkki. Se voi olla myös merkkijono  -
  429. jotkin funktiot palauttavat merkkijonon.
  430.  
  431. Rivinvaihtoon on käytettävä CRLF:ää eli tässä tapauksessa "\(13)\(10)", mikä  on
  432. hieman hankalaa. Värejä voi tulostuksessa käyttää  ESCAPE-koodin  avulla,  kuten
  433. normaalistikin ANSI:n mukaan. ESCAPE-koodin saa merkkijonoon  laittamalla  sinne
  434. "\(27)".
  435.  
  436. Sulkeiden sisällä voi olla myös kokonainen lause, jonka arvo sisällytetään merk-
  437. kijonoon. Lause voidaan suorittaa käynnistysvaiheessa tai joka kerta  uudelleen.
  438. Mikäli se halutaan suoritettavaksi vain kerran,  sitä  tulee  edeltää  avainsana
  439. "PARSE". Lauseen arvo voi olla myös merkkijono, jolloin se liittyy  varsinaiseen
  440. merkkijonoon siihen kohtaan, jossa lause on. 
  441.  
  442.  
  443. {3Ohjelmointi menukielellä
  444. {3------------------------
  445.  
  446. Menukielellä voi tehdä miltei samat asiat  kuin  Rexx-ohjelmassa.  Jotkin  asiat
  447. ovat helpommin tehtävissä, jotkin vaikeammin ja jotkut  ovat  jopa  mahdottomia.
  448. Menukieli sopii lyhyiden koodien tekemiseen ja yksinkertaisten komentojen imple-
  449. mentointiin. Vähänkään monimutkaisemmat toiminnot kannattaa tehdä Rexx-kielellä.
  450. Rexx-ohjelman voi helposti ajaa menukoodista  dos()-funktiolla.  Rexx()  ei  ole
  451. tähän tehtävään oikea funktio, koska se lähettää Rexx-komennon ohjelmalle,  eikä
  452. aja mitään ohjelmaa.
  453.  
  454. Komentojen nimeämiselle ja lyhennyksille kannattaa  omaksua  jokin  tyyli,  joka
  455. toistuu läpi valikoiden. Komentojen nimet voivat  esimerkiksi  olla  yksittäisiä
  456. verbejä tai sitten aina tekemistä ja sen kohdetta kuvaava predikaatin ja  objek-
  457. tin yhdistelmä, jonka lyhenne tulee aina kummastakin sanasta.
  458.  
  459. Useimmiten komennot kannattaa tehdä tiettyyn valikkoon. Hirveää määrää globaale-
  460. ja komentoja tulee välttää. Usein komennot liittyvät esimerkiksi viesteihin  tai
  461. tiedostoihin, joten niiden sijoitusvalikko on selvä. Itselläni on joukko yleisiä
  462. komentoja, jotka eivät liity mihinkään erityisesti, joten tein niille oman  Ext-
  463. ras-valikon. Minulla on komentoja esimerkiksi koneen muistin  ja  prosessilistan
  464. tutkailuun sekä ajan kyselyyn ja ohjelmiston versionumeron tiedusteluun.  Lähde-
  465. koodeja näihin on viimeisessä luvussa.
  466.  
  467. Voi myös olla, että teet jonkin erityisen palvelun purkkiisi. Tällöin on kannat-
  468. tavaa tehdä sille oma valikko. Minulla on Saku-valikko, jossa voi lukea linjalla
  469. aina uusimman Sakun artikkeleita sekä imeä niitä yksittäin. Tällaisia toimintoja
  470. varten on yleensä syytä tehdä oma valikko. Ihan muutamaa komentoa varten ei niin
  471. kuitenkaan kannata tehdä.
  472.  
  473. Tässä vaiheessa on jo syytä antaa vähän muutakin  kuin  irrallisia  esimerkkejä.
  474. Tässä on esimerkki oman valikon käynnistämiseen: 
  475.  
  476. Command "extras" (N:1) {
  477.     rexx("LogEntry Extras");
  478.     menu("Extras.menu");
  479. };
  480.  
  481. Tämä on yksinkertainen komento, jossa vaihdetaan  valikkoa.  Komennon  ajamiseen
  482. riitää ensimmäinen merkki, E. Tässä on ensimmäinen kunnollinen  komennon  lähde-
  483. koodi tuosta Extras-valikosta: 
  484.  
  485. Command "lc" {
  486.     rexx("LogEntry Viewing last callers");
  487.     rexx("SendASCII Text/LastCallers.txt");
  488.     rexx("SendModem \(13)\(10)");
  489. };
  490.  
  491. Janne Sirenin LastCallers-ohjelma, jonka käyttämisen kuvasin artikkelin  edelli-
  492. sessä osassa, ylläpitää listaa viimeisistä soittajista tiedostossa Text/LastCal-
  493. lers.txt. Komennolla lc käyttäjä voi nyt katsoa edelliset soittajat myös purkis-
  494. sa erittäin yksinkertaisella koodilla.  Kaikkien  komentojen  on  syytä  lopuksi
  495. lähettää rivinvaihto eli CRLF, koodit 13 ja 10.
  496.  
  497. Käyttäjästä voisi olla  mukavaa  saada  jotenkin  apua  hankalampien  komentojen
  498. käyttämisessä. Yksi mahdollisuus on tehdä Help-komento, jolle annetaan  komennon
  499. nimi, josta halutaan apua. Tällainen tosin kannattaisi tehdä Rexx-kielellä.  Oh-
  500. jelma voisi käyttää kullekin valikolle vaikkapa helppitiedostoja, joista se  tu-
  501. lostaisi halutun komennon osuuden rivipohjaisesti. Toinen tapa on antaa lyhyesti
  502. tietoa, kun komennolle annetaan  parametriksi  kysymysmerkki,  vastaavasti  kuin
  503. shellissä. Itselläni jokainen komento antaa kysymysmerkillä formaattinsa,  mutta
  504. apu voi olla muutakin: 
  505.  
  506. Command "jokainenkomento" {
  507.     if(arg(1) == "?") {
  508.         rexx("SendModem <Jonkinlaista apua>\(13)\(10)");
  509.         break;
  510.     }
  511.     ...
  512. };
  513.  
  514. Järjestelmän toiminnasta kertovia  komentoja  saa  helposti  aikaiseksi  useita.
  515. Tässä on yksi: 
  516.  
  517. Command "sys" {
  518.     rexx("LogEntry Showing system information");
  519.     dos("CPU >TECHIO:\(node())");
  520. };
  521.  
  522. Käyttäjälle ajetaan komento CPU, jonka tuloste ohjataan TechIO:n  avulla  modee-
  523. mille. Tuloste voi olla esimerkiksi:
  524.  
  525. System: 68030 68882 FastROM (INST: Cache Burst) (DATA: Cache NoBurst)
  526.  
  527. Moni käyttäjä ei ehkä ole tällaisesta tiedosta kiinnostunut, mutta heitä kuiten-
  528. kin on. Eri asia on, haluaako tästä tehtävän lokimerkintää. Minulla tehdään var-
  529. sin monipuoliset lokimerkinnät, mutta joku muu voi haluta  vähemmän  merkintöjä.
  530. Minulla ei käyttäjän tarvitse kuin tulla sisään ja katsella ympärilleen kymmeni-
  531. sen minuuttia, niin loki kasvaa kilokaupalla... Komento VER  kertoo  ohjelmiston
  532. versionumeroita: 
  533.  
  534. Command "version" (N:3 G:) {
  535.     rexx("LogEntry Inquiring software version");
  536.     dos("Version >TECHIO:\(node())");
  537.     dos("Version BBS:Bin/TechCon >TECHIO:\(node())");
  538.     dos("Version BBS:Bin/StarTech >TECHIO:\(node())");
  539. };
  540.  
  541. Tuota viimeistä ei tietenkään kannata  ajaa,  jos  käynnistää  nodensa  jollakin
  542. muulla ohjelmalla. Tällöin komennon voi korvata asianmukaisella komennolla, esi-
  543. merkiksi "Version Bin:TrapDoor", jolloin komennon tuloste voisi olla: 
  544.  
  545. Kickstart 40.9, Workbench 40.6
  546. TechCon 0.93
  547. TrapDoor 4.279
  548.  
  549. Ehkei tuo olekaan hyvä idea. Tietääkseni TrapDoorini versio on 1.83  -  jostakin
  550. syystä se kuitenkin kertoo Version-komennolle ihan muuta. Valikkoon on  muistet-
  551. tava laittaa mahdollisuus palata päävalikkoon: 
  552.  
  553. Command "quit" {
  554.     Menu("Main.menu");
  555. };
  556.  
  557.  
  558. {3Lisää TechnoBBS-komentoja ja -funktioita
  559. {3----------------------------------------
  560.  
  561. Tässä luvussa esittelen tukun tehokkaita TechnoBBS:n  komentoja  ja  funktioita.
  562. Yksityiskohtaisessa käsittelyssä ovat komennot ListFiles ja SelectFiles. Seuraa-
  563. vassa luvussa on valmiita menukoodeja uusien komentojen aikaansaamiseksi. Niissä
  564. hyödynnetään tässä luvussa esiteltäviä komentoja ja funktioita.
  565.  
  566. ListFiles on erittäin tehokas tiedostojenlistauskomento. Sillä voi listata kaik-
  567. ki, tietyn ajankohdan jälkeen upitut tai kuvaukseen  osuvat  tiedostot  kaikilta
  568. tai tietyltä alueelta tai yhdeltä alueelta ja  kaikilta  sen  ala-alueilta  joko
  569. erikseen tai yhdessä vanhimmasta uusimpaan, uusimmasta vanhimpaan, aakkosjärjes-
  570. tyksessä tai käänteisessä aakkosjärjestyksessä. 
  571.  
  572. Tässä ovat komennon optiot:
  573.  
  574.          A        Kaikki tiedostot kaikilta alueilta
  575.          E        Kaikki tiedostot tällä alueella ja sen ala-alueilla
  576.          C        Jatkuva listaus, ei promptia
  577.          B        Ei ala-alueita
  578.          I        Ei hakemistoja, kaikkien alueiden tiedostot samassa listassa
  579.          N        Ei tyhjiä hakemistoja
  580.          M        Näytä tiedostojen numerot
  581.          L        Näytä pitkät kuvaukset
  582.          G        Kokonaiset aluenimet (todelliset hakemistopolut)
  583.  
  584. Näiden optioiden lisäksi on viisi optiota, joille annetaan myös parametri:
  585.  
  586.          O <formatointimerkkijono>
  587.          P <pattern>
  588.          F <string>
  589.          D <date>
  590.          S <method>
  591.  
  592. Komennon tulosteen voi formatoida haluamallaan  tavalla.  Optioon  O  liitetyssä
  593. formatointimerkkijonossa voi käyttää seuraavia määrittelyjä: 
  594.  
  595.          %n       tiedoston nimi
  596.          %s       tiedoston pituus
  597.          %c       kommentti
  598.          %d       päiväys
  599.          %t       aika
  600.          %a       päivä
  601.          %p       protection-bitit
  602.  
  603. Tiedostoja voi myös etsiä komennon  avulla.  Tiedostoja  voi  listata  määritte-
  604. lemällä patternin (optio P), johon niitä verrataan. Voi myös antaa  merkkijonon,
  605. jota etsitään tiedoston nimestä ja kuvauksesta (ei kuitenkaan  pitkästä  kuvauk-
  606. sesta) ja listataan ne, joista se  löytyi  (optio  F).  Optiolla  D  voit  antaa
  607. päiväyksen, josta lähtien tiedostot listataan. Päiväys voi olla joko standardis-
  608. sa dd-mmm-yy -muodossa tai päivien määrä tammikuun ensimmäisestä päivästä vuonna
  609. 1978 (TechnoBBS:n yleinen päivämääräformaatti). 
  610.  
  611. Optiolla S (sort) voit valita, miten tiedostot listataan:
  612.  
  613.          N        Uusimmasta vanhimpaan
  614.          O        Vanhimmasta uusimpaan
  615.          A        Aakkosjärjestyksessä (suositeltavin tapa)
  616.          Z        Käänteisessä aakkosjärjestyksessä
  617.  
  618. Seuraavassa luvussa on esimerkki ListFiles-komennon implementoinnista  purkkiin.
  619. Annan suoraan koko lähdekoodin omassa purkissani olevaan  List-komentoon.  Sillä
  620. voi myös käynnistää kokoruututiedostolistaimen, joka aktivoituu  komennolla  Se-
  621. lectFiles. Myös se on hyvin tehokas tiedostojenlistauskomento ja  pystyy  samaan
  622. kuin ListFiles, mutta se  on  interaktiivinen,  ja  sen  avulla  voi  seikkailla
  623. alueelta toiselle ja myös imeä tiedostoja ja merkitä niitä. 
  624.  
  625. SelectFilesin optiot poikkeavat hieman ListFilesin optioista:
  626.  
  627.          A        Kaikki tiedostot, kaikki ala-alueet
  628.          E        Kaikki tiedostot tällä alueella ja sen ala-alueilla
  629.          H        Näytä nykyisen tiedoston nimi vahvennetulla (katso alla)
  630.          C        Näytä ala-alueet ja salli alueen vaihto (korvaa E:n)
  631.  
  632. Kokoruututiedostonvalitsin toimii niin, että se näyttää ruudullisen  tiedostoja,
  633. ja listauksessa voi liikkua nuolinäppäimillä. Normaalisti  kursorin  paikka  il-
  634. maistaan ">"-merkillä vasemmassa reunassa, mutta sen sijaan voidaan käyttää  ni-
  635. men vahventamista (optio H), mikä ei kuitenkaan ole  suotavaa.  Optiot  A  ja  E
  636. näyttävät tiedostot samassa listauksessa. Paras  vaihtoehto  on  optio  C,  joka
  637. näyttää tiedostot alueittain ja antaa mahdollisuuden vaihtaa aluetta.
  638.  
  639. Normaalien Download- ja Upload-komentojen lisäksi TechnoBBS:ssä on  mahdollisuus
  640. siirtää tiedostoja yhtä aikaa kumpaankin suuntaan. Sitä varten on  olemassa  ko-
  641. mento DlAndReceive. Sillä voi imeä ja uppia yhtä aikaa. Ensin imettävät  tiedos-
  642. tot tulee merkitä  ja  sen  jälkeen  vaihtaa  alueelle,  jolle  uppii.  Komennon
  643. käyttäminen edellyttää kaksisuuntaista protokollaa. Sellainen on ainakin DModem,
  644. joka on myös yksisuuntaisena nopeampi kuin ZModem. Jostakin syystä  vain  harvat
  645. purkit   tukevat    sitä.    Esimerkki    DModemin    alustusmerkkijonoksi    on
  646. "XY,LY,IY,B2048".
  647.  
  648. Lisäksi tiedostoja voi siirtää funktioilla SendFiles() ja ReceiveFiles(). Recei-
  649. veFiles():iä käytetään esimerkiksi vastaanotettaessa replypakettia.  SendFiles()
  650. taas on käytössä tekstikirjastosovelluksessani, jonka lähdekoodi on  seuraavassa
  651. luvussa. Nodella on lähetettävistä tiedostoista lista, jonka voi  tyhjentää  ko-
  652. mennolla ClearFiles. Tämän jälkeen lähetettävät tiedostot lisätään listaan yksi-
  653. tellen komennolla AddFile. Lopuksi ne lähetetään kutsumalla SendFiles():iä, joka
  654. palauttaa nollan, jos siirto epäonnistui.
  655.  
  656. Viestipuolella on monia kiinnostavia komentoja. Yksi niistä on  ReWriteMsg.  Sen
  657. avulla voidaan toteuttaa BBBS:n dup-komentoa vastaava komento. ReWriteMsg  <msg>
  658. lataa määritellyn viestin editoriin, jolloin sitä voidaan muutella ja  tallentaa
  659. siitä uusi versio. Tämä uusi versio jää voimaan ja vanha tuhotaan.  Viesti  voi-
  660. daan tuhota komennolla KillMsg. Viestejä  ei  kuitenkaan  koskaan  varsinaisesti
  661. poisteta viestikannasta, koska se olisi liian hidasta, vaan ne  ainoastaan  mer-
  662. kitään kuolleiksi.
  663.  
  664. Ihmettelen kyllä, miksei sitten ole olemassa komentoa UnkillMsg,  koska  viestin
  665. herättäminen kuolleista olisi erittäin yksinkertaista. Olen kuullut, että  vies-
  666. tejä tapetaan helposti vahingossa, joten  tällainen  olisi  tarpeen.  ReWriteMsg
  667. edellyttää käyttäjältä viestineditointioikeuksia, jos viesti ei  ole  hänen  it-
  668. sensä kirjoittama. KillMsg ei tee tarkistuksia, joten on syytä itse varoa  anta-
  669. masta kaikkien käyttää sitä.
  670.  
  671. ConvertMsg <msg> <set> konvertoi viestikannassa olevan  viestin  annetusta  mer-
  672. kistöstä ISO:ksi. Tämä on kätevä toiminto, jos esimerkiksi käyttäjä  on  uppinut
  673. replynsä väärällä merkistöllä. Tällöin tosin  inbound-konversiossa  merkit  ovat
  674. saattaneet kääntyä väärin. TechnoBBS tarjoaa erittäin  hyvät  merkistökonversio-
  675. toiminnot. Hankalinta onkin saada käyttäjät valitsemaan oikea merkistö!
  676.  
  677. Komennolla EditMsg <msg> pääsee editoimaan viestin statusbittejä. Se vastaa  E:n
  678. painamista viestejä lukiessa. Bittieditorissa voi  esimerkiksi  tehdä  viestistä
  679. yksityisen. Suositeltava tapa viestien lukemiseen on ReadNext. Se aloittaa vies-
  680. tien lukemisen ensimmäisestä lukemattomasta viestistä ja lukee ne loppuun kysei-
  681. sellä alueella. Toinen vaihtoehto on ReadAllNew, joka toimii muuten samoin, mut-
  682. ta lukee uudet viestit kaikilta alueilta.
  683.  
  684. Vielä  yksi  komento  on  syytä  ottaa  puheeksi.  ShowMessage  <text>   näyttää
  685. käyttäjälle lyhyen tiedonannon. Sen  perään  tulostetaan  prompti  ja  odotetaan
  686. näppäimenpainallusta. Tämä komento on tarkoitettu käytettäväksi noden  laukaise-
  687. missa  asynkronisissa  ohjelmissa.  Esimerkiksi  TechQWK  ja  TechWWF  käyttävät
  688. ShowMessagea kertoessaan, että viestipaketti on valmis.
  689.  
  690. Funktioiden puolella tulee ensiksi kiinnostavista vastaan GetDateVal().  Se  pa-
  691. lauttaa systeemikellon ajan päivien  määränä  tammikuun  ensimmäisestä  päivästä
  692. vuonna 1978. Tämä on TechnoBBS:n käyttämä päivämääräformaatti.  Tiedostoalueista
  693. saa tietoa funktioilla GetLowMsg(), GetHighMsg() ja GetHighRead(). Funktiot  pa-
  694. lauttavat alueen ensimmäisen ja viimeisen viestin numeron sekä viimeisen  luetun
  695. viestin numeron. Näitä käytetään useissa seuraavassa luvussa  esiteltävissä  ko-
  696. mennoissa.
  697.  
  698. Viestialueen ja alueryhmän numeron saa selville funktioilla GetMsgArea() ja Cur-
  699. rentSIG(), alueen nimen funktiolla GetMsgAreaName(). SIG:n nimen pitäisi selvitä
  700. funktiolla SIGName(), mutta se ei toimi. Ainakin minulla se palauttaa  aina  en-
  701. simmäisen SIG:n nimen, joten siitä ei ole hyötyä.  Käyttäjän  oikeudet  jollekin
  702. alueelle voi tarkistaa funktiolla HasMsgAcc().  Se  palauttaa  arvon  yksi,  jos
  703. käyttäjällä on pääsy alueelle.
  704.  
  705. Viimeinen tarkastelun kohteeksi pääsevä funktio on SIGtoReal(). Se muuttaa anne-
  706. tun viestialueen numeron todelliseksi aluenumeroksi. Sille annetaan alueen nume-
  707. ro SIG:n sisällä, ja se palauttaa numeron, joka vastaa msga<n>.dat- sekä  muiden
  708. tiedostojen numerointia. Jotkin komennot haluavat parametrinään alueen  todelli-
  709. sen numeron. Tällainen on esimerkiksi MoveMsg. Tästä komennosta on lisää  tietoa
  710. seuraavassa luvussa, jossa käsittelen lähemmin myös muutamia muita komentoja.
  711.  
  712.  
  713. {3Uusia komentoja
  714. {3---------------
  715.  
  716. Tässä viimeisessä luvussa julkaisen useita valmiita komentoja vapaasti portatta-
  717. vaksi mihin tahansa purkkiin. Jos käytät tässä julkaistua koodia, kerro kaikille
  718. siitä! 
  719.  
  720. Aloitamme omalla versiollani tiedostojenlistauskomennosta. Komento korvaa  alku-
  721. peräisen list-komennon. 
  722.  
  723. Command "list" (N:1) {
  724.     $sel = 0;
  725.     $selmode = "-C";
  726.     $sort = "-SA";
  727.     $an = 1;
  728.     $opt = "";
  729.     if(arg(1) != "") while(arg($an) != "") {
  730.         $curr = upper(arg($an));
  731.         $temp = "";
  732.         if($curr == "ALL") {
  733.             $temp = "-E";
  734.             $selmode = "-E";
  735.         }else if($curr == "SELECTOR") $sel = 1;
  736.         else if($curr == "FILES") $temp = "-I";
  737.         else if($curr == "DAYS") {
  738.             $an = $an + 1;
  739.             $temp = "-D\(GetDateVal() - arg($an))"
  740.         } else if($curr == "SINCE") {
  741.             $an = $an + 1;
  742.             $temp = "-D\(arg($an))"
  743.         } else if($curr == "NEWFILES") {
  744.             $temp = "-D\(GetUserMisc("LASTFSCAN"))";
  745.             SetUserMisc("LASTFSCAN",GetDateVal());
  746.         } else if($curr == "ALPHA") $sort = "-SA";
  747.         else if($curr == "REVERSE") $sort = "-SZ";
  748.         else if($curr == "CRON") $sort = "-SO";
  749.         else if($curr == "REVCRON") $sort = "-SN";
  750.         else if($curr == "FIND") {
  751.             $an = $an + 1;
  752.             $temp = "-F'\(arg($an))'"
  753.         } else $temp = "-P\($curr)";
  754.         if($temp != "") $opt = "\($opt) \($temp)";
  755.         $an = $an + 1;
  756.     }
  757.     if($sel) {
  758.         rexx("LogEntry Selecting");
  759.         rexx("SelectFiles \($selmode) -H \($opt) \($sort)");
  760.     } else {
  761.         rexx("LogEntry Listing");
  762.         rexx("ListFiles -G -M -L \($opt) \($sort)");
  763.     };
  764. };
  765.  
  766. Komennon formaatti on tällainen:
  767.  
  768. LIST [ALL] [FILES] [NEWFILES] [DAYS <n>] [SINCE <date>] [SELECTOR]
  769.      [ALPHA|REVERSE|CRON|REVCRON] [FIND <string>] [<Pattern>]
  770.  
  771. Tämä on epäilemättä eräs tehokkaimmista ja  monimutkaisimmista  list-komennoista
  772. kautta purkkimaailman. Komennolla voi tehdä melkein mitä vain - listata  tiedos-
  773. toja, etsiä niitä, imeä niitä, ottaa newfiles-listauksen jne. Tässä on  selvitys
  774. komennon optioista: 
  775.  
  776.          ALL               Kaikki tiedostot kaikilta alueilta
  777.          FILES             Vain tiedostot (ALL-option kanssa myös ala-alueiden)
  778.          NEWFILES          Uudet tiedostot (viime filescanin jälkeen upitut)
  779.          DAYS <n>          N päivää sitten ja sen jälkeen upitut tiedostot
  780.          SINCE <date>      Sama juttu, toimii aivan kuin DOS-Listissä
  781.          SELECTOR          Käytä tiedostojen näyttämiseen FSE-valitsinta
  782.          ALPHA             Näytä tiedostot aakkosjärjestyksessä (oletus)
  783.          REVERSE           Käänteinen aakkosjärjestys
  784.          CRON              Aikajärjestys
  785.          REVCRON           Käänteinen aikajärjestys
  786.          FIND <string>     Vain tiedostot, joissa on annettu merkkijono
  787.          <Pattern>         Pattern, johon täsmäävät tiedostot listataan
  788.  
  789. FIND-option yhteydessä annettua merkkijonoa siis etsitään tiedoston  nimestä  ja
  790. lyhyestä kuvauksesta - ei tiedostosta itsestään. Merkkijonossa ei saa olla väli-
  791. lyöntejä. Optiolla ALL listataan kaikkien  alueiden  tiedostot  aluekohtaisesti.
  792. FILES-optiolla listataan vain tiedostot, eikä ala-alueita näytetä ollenkaan. Yh-
  793. dessä käytettynä ALL- ja FILES-optiot saavat aikaan sen, että kaikkien  alueiden
  794. tiedostot näytetään samassa  listassa!  Kokoruutuvalitsinta  käytettäessä  tähän
  795. riittää pelkkä ALL-optio - itse asiassa sen kanssa FILES-optiolla ei ole  merki-
  796. tystä, koska SelectFiles ei tue I-optiota.
  797.  
  798. Virheellisistä tai tunnistamattomista optioista ei huomauteta, vaan ne ohitetaan
  799. olankohautuksella. Parametrillisten optioiden parametrien olemassaoloa  ei  tar-
  800. kisteta, mutta en usko TechnoBBS:n siihen kaatuvan, vaikka jonkin  option  para-
  801. metri puuttuisikin, mutta tietysti käyttäjän tulee varmistua siitä, että  kaikki
  802. parametrit annetaan. BBS:ni on "high-end",  joten  myös  käyttäjien  tulee  olla
  803. asiantuntevia!
  804.  
  805. Muutama esimerkki kertoo vähintään yhtä paljon kuin 1024 sanaa: 
  806.  
  807. list all files newfiles
  808.  
  809. Näyttää uudet tiedostot samassa listassa (files-optio) kaikilta alueilta  (all).
  810. Itse asiassa kaikki alueet ei ole absoluuttinen käsite. Kaikilla  alueilla  tar-
  811. koitetaan sitä aluetta, jolla käyttäjä on sekä sen ala-alueita.  ListFiles-  tai
  812. SelectFiles-komennolle (kumpaa käytetään, ratkeaa siitä, onko komentorivillä op-
  813. tio SELECTOR) annetaan all-option ollessa määritelty optio E. Optiota A ei  tämä
  814. komento koskaan käytä. 
  815.  
  816. list days 30 cron
  817.  
  818. Listaa viimeisen kuukauden aikana upitut tiedostot aikajärjestyksessä. 
  819.  
  820. list all find Tech
  821.  
  822. Listaa kaikki tiedostot, joiden nimessä tai lyhyessä  kuvauksessa  esiintyy  sa-
  823. nanpätkä "Tech". Tiedostoja etsitään kaikilta alueilta ja ne näytetään  aluekoh-
  824. taisesti aakkosjärjestyksessä. 
  825.  
  826. list all files since 01-Jan-95
  827.  
  828. Voiko enää inhimillisemmäksi BBS:n  käyttäminen  mennä?  Kyllä  voi,  mutta  sen
  829. käsittely jääköön toistaiseksi. Tämä komento vastaa itse  asiassa  täysin  DOSin
  830. List-komennon toimintaa. Komento luettelee kaikki tällä  alueella  ja  sen  ala-
  831. alueilla olevat tiedostot, jotka on upittu tänä vuonna.
  832.  
  833. Olen kirjoittanut useimmat TechnoBBS:n komennot suurilta osin tai täysin  uusik-
  834. si. Vakiona TechnoBBS:ään kuuluvat  viestikomennot  ovat  aika  sekavia.  Jotkin
  835. käyttävät optioita, mutta ne ovat kryptisiä  Unix-tyylisiä  optioita.  Tässä  on
  836. esimerkiksi vielä viestivalikon list-komentoni: 
  837.  
  838. Command "list" (N:1) {
  839.     $area = GetMsgArea();
  840.     if(GetHighRead($area) == GetHighMsg($area)) $defstart = GetLowMsg($area);
  841.         else $defstart = GetHighRead($area) + 1;
  842.     if(arg(1) == "") $start = AskInput("\(27)[32mFrom message? \(27)[0m",
  843.         $defstart, 10, "NUMERIC");
  844.         else $start = arg(1);
  845.     if(arg(2) == "") $end = AskInput("\(27)[32mTo message? \(27)[0m",
  846.         GetHighMsg(GetMsgArea()), 10, "NUMERIC");
  847.         else $end = arg(2);
  848.     rexx("SendModem \(13)\(10)";
  849.     rexx("ListMessages \($start) \($end)");
  850. };
  851.  
  852. Komento ottaa parametreikseen listauksen alun ja lopun. Mikäli niitä  ei  määri-
  853. tellä, ne kysytään. Oletuksena tarjotaan promptissa aloitusviestiksi ensimmäistä
  854. lukematonta viestiä tai, jos alueen kaikki viestit on luettu,  ensimmäistä  ole-
  855. massa olevaa viestiä sekä lopetusviestiksi viimeistä viestiä.  Samoja  oletuksia
  856. tarjoaa myös esimerkiksi Find-komentoni. Lisäksi minulla on  BBBS-tyylinen  Mark
  857. Group -toiminto, joka käyttää samantapaista logiikkaa. En listaa enää muita  va-
  858. kiokomentoja, mutta niiden lähdekoodia saa minulta pyytämällä.
  859.  
  860. Siirtyminen viestivalikkoon ei ole niin helppoa kuin voisi luulla. Tässä on  pa-
  861. ranneltu versio tehtävän tekevästä komennosta: 
  862.  
  863. Command "messages" (N:1) {
  864.     rexx("LogEntry Messages");
  865.     rexx("SelectSIG \(arg(1))");
  866.     if(!CurrentSIG()) rexx("SelectSIG 1");
  867.     rexx("SendModem \(12)");
  868.     rexx("SelectSIGArea \(arg(2))");
  869.     menu("Msg.menu");
  870. };
  871.  
  872. Tällä komennolla varmistutaan siitä, että päästään viestivalikkoon,  vaikka  ty-
  873. perä käyttäjä painaisikin vain enteriä tajuamatta valita SIG:iä tai viestialuet-
  874. ta. Alkuperäisellä koodillahan silloin silloin takaisin päävalikkoon, mutta tämä
  875. koodi vie silloin ensimmäisen SIG:n ensimmäiselle viestialueelle,  joka  yleensä
  876. on postialue, tai sinne, missä viimeksi on oltu.
  877.  
  878. Viestit luetaan kätevästi painamalla enteriä, mutta miten olisi  viestivalikkoon
  879. meneminen enterin painalluksella, kuten BBBS:ssä? Kas tässä: 
  880.  
  881. Command "" {
  882.     $sig = GetUserMisc("DEFAULTSIG");
  883.     if($sig == "") $sig = 1;
  884.     if(($sig < 1) | ($sig > 6) {
  885.         rexx("SendModem\(27)[31mYour default SIG setting is invalid.
  886.         \(13)\(10)\(13)\(10)\(27)[33m
  887.         Using default default setting.\(13)\(10)");
  888.         $sig = 1;
  889.     }
  890.     rexx("LogEntry Messages");
  891.     rexx("SelectSIG \($sig)");
  892.     rexx("SelectSIGArea 1");
  893.     menu("Msg.menu");
  894. };
  895.  
  896. Tämä tulee siis päävalikkoon, ja se vie käyttäjän  viestivalikkoon  painettaessa
  897. enteriä tyhjälle riville. Näin viestejä päästään lukemaan loginin  jälkeen  kah-
  898. della enterin painalluksella! Laita ennen kääntämistä  ensimmäinen  rexx()-funk-
  899. tiokutsu yhdelle riville.
  900.  
  901. Komento vaatinee hieman selvitystä. Aikaisemmin minulla oli siinä vain  "Select-
  902. SIG 1", mutta nyt SIG:n numero otetaan käyttäjämuuttujasta DEFAULTSIG. Tälle tu-
  903. li tarvetta, koska haluan yleensä päästä suoraan SakuNet-alueille, mikä näin on-
  904. nistuu helposti laittamatta sitä kiinteäksi  oletusarvoksi.  Riittää,  että  DE-
  905. FAULTSIG:n arvo on 3, joka on SakuNetin SIG:n numero. Tämän asettamiseksi tarvi-
  906. taan tietysti sopiva koodi esimerkiksi terminaaliasetusvalikkoon: 
  907.  
  908.     des = GetUserMisc(ln, "DEFAULTSIG")
  909.     select
  910.         when des = 1 then des = "Yleiset alueet"
  911.         when des = 3 then des = "SakuNet"
  912.         when des = 6 then des = "WebNet"
  913.         otherwise des = "<NONE>"
  914.         end
  915.     SendModem "12     "||CYAN||"Default SIG: "||WHITE||des||CRLF
  916.  
  917. Tämä koodi tulee sinne alkuun, missä tulostetaan olemassaolevat  valinnat.  Seu-
  918. raava koodi tulee loppuun. Itselläni valinnan numero on  12,  mutta  siihen  voi
  919. laittaa muutakin, mikä sitten sattuu olemaan ensimmäinen vapaa numero.  SIG-koo-
  920. deistani on tarkoituksella poistettu SIG:t 2, 4 ja 5. 
  921.  
  922.         when cmdid = "12" then do
  923.             dez = GetUserMisc(ln, "DEFAULTSIG")
  924.             if dez = "" then dez = 1
  925.             des = "<NONE>"
  926.             do while des = "<NONE>"
  927.                 SendModem CRLF
  928.                 SendASCII "Text/DefaultSIG.txt"
  929.                 des = AskInput(ln, "Select: ", "", 2, "NUMERIC");
  930.                 select
  931.                     when des = "1" then nop
  932.                     when des = "2" then nop
  933.                     when des = "3" then nop
  934.                     when des = "4" then nop
  935.                     when des = "5" then nop
  936.                     when des = "6" then nop
  937.                     when des = "" then des = dez
  938.                     otherwise des = "<NONE>"
  939.                     end
  940.                 end
  941.  
  942. Koodi toimii vastaavasti kuin esimerkiksi offlinerin valinta (tulee  myöhemmin).
  943. Ovelalla  manipuloinnilla  saadaan   käyttäjä   valitsemaan   oletusarvo   hänen
  944. tietämättään. Tosin myös koodi, joka arvoa käyttää, osaa valita oletusoletuksen,
  945. jos arvo on väärä tai puuttuu. Ohjeeksi lähetetään tiedosto DefaultSIG.txt: 
  946.  
  947.     The following SIGs are available in this system:
  948.  
  949.        1 Yleiset alueet
  950.        3 SakuNet
  951.        6 WebNet
  952.  
  953. Select one or press enter for no change or default selection.
  954.  
  955. Ensimmäinen varsinainen uusi komento tulee tässä. Komennolla voi siirtää viestin
  956. toiselle alueelle: 
  957.  
  958. Command "move" (N:2) {
  959.     if(GetUserAccess() < 1000) {
  960.         rexx("SendModem \(27)[31mNo access\(13)\(10)");
  961.         rexx("LogEntry Attempted to move a message");
  962.         break;
  963.     }
  964.     $msg = arg(1);
  965.     $area = arg(2);
  966.     if($msg == "") {
  967.         $msg = AskInput("Message number: ", "", 10, "NUMERIC");
  968.     }
  969.     if($area == "") {
  970.         $area = AskInput("Destination area: ", "", 10, "NUMERIC");
  971.         rexx("SendModem \(13)\(10)\(13)\(10)");
  972.     }
  973.     if($msg != "") if($area != "") {
  974.         rexx("SendModem Moving message.\(13)\(10)");
  975.         rexx("MoveMsg \($msg) \($area)");
  976.         rexx("KillMsg \($msg)");
  977.         rexx("LogEntry Moved message \($msg) from \(GetMsgAreaName(
  978.             GetMsgArea())) to \(GetMsgAreaName($area)) as #\(
  979.             GetHighMsg($area))\(13)\(10)");
  980.     }
  981. };
  982.  
  983. En tiedä, miten TechMenuComp suhtautuu tuolla tavalla jaettuihin riveihin, mutta
  984. uskoisin, että viimeinen rexx()-funktiokutsu kannattaa palauttaa yhdelle  rivil-
  985. le. Jaoin sen kahdelle riville  taittoteknisistä  syistä...  Komento  varmistaa,
  986. että käyttäjällä on SysOp-oikeudet (oikeustaso  =>  1000).  Komennolle  annetaan
  987. siirrettävän viestin numero ja alue, jolle viesti siirretään. Jos niitä ei anne-
  988. ta komentorivillä, ne kysytään erikseen.
  989.  
  990. Kohdealueen numero on tässä nyt alueen  todellinen  numero  sellaisena  kuin  se
  991. msg<n>.dat-tiedoston  nimessä  on.  Tätä  voisi  helpottaa  käyttämällä   SIGto-
  992. Real()-funktiota, mutta ainakin minulla on tarve siirtää joskus viestejä SIG:stä
  993. toiseen,  joten  tuollaista  ei  voi  käyttää,  koska  siirto  estyisi.  Viestin
  994. siirtämisen jälkeen alkuperäinen tuhotaan, koska MoveMsg ei tee sitä itse.  Itse
  995. asiassa se onkin toiminnaltaan pikemminkin CopyMsg...
  996.  
  997. Jälleen tehdään perusteellinen lokimerkintä... Alkuperäisen viestin numerolla ei
  998. tosin enää ole merkitystä, eikä uudenkaan viestin numero  välttämättä  ole  aina
  999. oikein, mutta mahdollisuus sen väärin menemiseen on hyvin pieni (jos jokin  toi-
  1000. nen node ehtii välissä laittaa kohdealueelle toisen viestin). 
  1001.  
  1002. TechnoBBS:ssä ei ole minkäänlaista viestialueiden läpikäyntikomentoa. Esimerkik-
  1003. si BBBS:ssä on hyvä show (conference status) -komento. Olen kirjoittanut vastaa-
  1004. van  TechnoBBS:lle.  Se  ei  ole  yhtä  monipuolinen,  koska  olisi  käytännössä
  1005. äärettömän hidasta ja miltei mahdotonta skannata lennossa, kuinka monta  viestiä
  1006. nimenomaiselle käyttäjälle on. Senhän saa selville mc-komennolla, joten alueilla
  1007. olevien viestien kokonäismäärä ja uusien viestien määrä riittänevät: 
  1008.  
  1009. Command "scan" (N:2) {
  1010.     $curr = GetMsgArea();
  1011.     $signum = arg(1);
  1012.     $mode = arg(2);
  1013.     if($signum == "") $signum = CurrentSIG();
  1014.     if(upper(arg(1)) == "NEW") {
  1015.         $signum = CurrentSIG();
  1016.         $mode = "NEW";
  1017.     }
  1018.     if($signum == 6) {
  1019.         $sig = "WebNet";
  1020.         $area = 101;
  1021.         $max = 130;
  1022.     } else if($signum == 3) {
  1023.         $sig = "SakuNet";
  1024.         $area = 31;
  1025.         if(GetUserAccess() > 999) $max = 64; else $max = 61;
  1026.     } else {
  1027.         $sig = "Yleiset alueet";
  1028.         $area = 3;
  1029.         $max = 12;
  1030.     }
  1031.     rexx("SendModem \(27)[32mScanning message areas: \(
  1032.         $sig)\(13)\(10)\(13)\(10)");
  1033.     rexx("LogEntry Scanning SIG \($sig)");
  1034.     $hightotal = 0;
  1035.     $msgtotal = 0;
  1036.     while($area <= $max) {
  1037.         $highread = GetHighRead($area);
  1038.         $highmsg = GetHighMsg($area);
  1039.         $areaname = "\(GetMsgAreaName($area)):";
  1040.         if($highmsg) {
  1041.             $doarea = 1;
  1042.             if($mode != "") if($highmsg == $highread) $doarea = 0;
  1043.             if(!(GetAreaMode($area))) $doarea = 0;
  1044.             if($doarea) {
  1045.                 rexx("SendModem \(27)[33m\(str($areaname,1,24))\(27)[0m\(
  1046.                     $highmsg - $highread) unread messages (\(
  1047.                     $highmsg) total, last read \($highread))\(13)\(10)");
  1048.                 $hightotal = $hightotal + $highread;
  1049.                 $msgtotal = $msgtotal + $highmsg;
  1050.             }
  1051.         }
  1052.         $area = $area + 1;
  1053.     }
  1054.     rexx("SendModem \(13)\(10)\(27)[33m\(str("Total:",1,24))\(27)[0m\(
  1055.         $msgtotal - $hightotal) unread messages (\(
  1056.         $msgtotal) total, read \($hightotal))\(13)\(10)");
  1057. };
  1058.  
  1059. Komento käy läpi halutun SIG:n alueet ja kertoo niistä tietoa. Tässä on esimerk-
  1060. kituloste: 
  1061.  
  1062. AZ.Muut:                4 unread messages (61 total, last read 57)
  1063. AZ.Net:                 43 unread messages (282 total, last read 239)
  1064. AZ.SciFi & Trek:        2 unread messages (22 total, last read 20)
  1065. AZ.Tee-se-itse:         0 unread messages (26 total, last read 26)
  1066. AZ.TV & Elokuvat:       0 unread messages (50 total, last read 50)
  1067. AZ.SAKU/Yleinen:        5 unread messages (28 total, last read 23)
  1068. AZ.SAKU/Info:           0 unread messages (21 total, last read 21)
  1069. AZ.SAKU/Ohjelmointi:    0 unread messages (6 total, last read 6)
  1070. AZ.SAKU/Toimitus:       0 unread messages (3 total, last read 3)
  1071. AZ.SAKU/Yhdistys:       0 unread messages (6 total, last read 6)
  1072.  
  1073. Total:                  54 unread messages (505 total, read 451)
  1074.  
  1075. Komento ottaa parametrinään skannattavan SIG:n  numeron.  Jos  sitä  ei  anneta,
  1076. skannataan se SIG, jossa ollaan. Lisäksi  komento  tunnistaa  option  NEW,  joka
  1077. määrittelemällä näytetään vain alueet, joilla on uusia viestejä. Option NEW  voi
  1078. antaa myös ilman SIG-numeroa. Koodissa on hieman logiikkaa. Vain alueet,  joille
  1079. käyttäjä on liittynyt ja joilla on viestejä, näytetään.
  1080.  
  1081. SIG:n nimet on asetettava käsin, koska SIGName  ei  toimi.  Lisäksi  SIG-numeron
  1082. vertailussa asetetaan  asianmukaiset  aluerajat.  Huomaa  SakuNetin  tapauksessa
  1083. vaihtoehtoinen loppumisalue. Lopussa on kolme  aluetta,  jotka  eivät  näy  lop-
  1084. pukäyttäjille, joten niitä ei myöskään näytetä skannatessa kuin SysOpille.
  1085.  
  1086. Janne Siren on tehnyt myös viestialuestatistiikkaa kertovan ohjelman.  Olen  vi-
  1087. rittänyt myös siihen SIG-pohjaisen toiminnan: 
  1088.  
  1089. Command "mstats" (N:2 G:) {
  1090.     $signum = arg(1);
  1091.     if($signum == "") $signum = CurrentSIG();
  1092.     if($signum == 6) {
  1093.         $sig = "WebNet";
  1094.         $area = 101;
  1095.         $max = 130;
  1096.     } else if($signum == 3) {
  1097.         $sig = "SakuNet";
  1098.         $area = 31;
  1099.         if(GetMask()&1024) $max = 64; else $max = 61;
  1100.     } else {
  1101.         $sig = "Yleiset alueet";
  1102.         $area = 3;
  1103.         $max = 12;
  1104.     }
  1105.     rexx("SendModem \(27)[32mViewing message area statistics for SIG \(
  1106.         $sig)\(13)\(10)\(13)\(10)");
  1107.     dos("RX >NIL: BBS:Rexx/MessageStats.rexx \(node()) \($area) \($max)");
  1108.     rexx("LogEntry Examining message area statistics for SIG \($sig)");
  1109. };
  1110.  
  1111. Tämä vaatii muutoksia myös itse ohjelmaan. Poista MessageStats.rexxistä  FirstA-
  1112. rea- ja LastArea-määrittelyt ja korvaa ln-määrittely komennolla: 
  1113.  
  1114. Parse ARG ln FirstArea LastArea .
  1115.  
  1116. Älä unohda lopussa olevaa pistettä. Näillä  muutoksilla  myös  MessageStats  käy
  1117. läpi vain yhden SIG:n, mikä on järkevin toimintatapa. 
  1118.  
  1119. TechnoBBS:n mukana ei tule myöskään tarpeellista SIG:n  vaihtokomentoa.  Seuraa-
  1120. valla koodinpätkällä homma hoituu: 
  1121.  
  1122. Command "sig" (N:1) {
  1123.     $sig = arg(1);
  1124.     $area = arg(2);
  1125.     if(($sig != "") && ($area == "")) $area = 1;
  1126.     rexx("SelectSIG \($sig)");
  1127.     if($area == "") {
  1128.         rexx("SendModem \(12)");
  1129.         rexx("SelectSIGArea");
  1130.     } else rexx("SelectSIGArea \($area)");
  1131.     rexx("LogEntry Message area: \(GetMsgAreaName(GetMsgArea()))");
  1132. };
  1133.  
  1134. Komennolle annetaan halutun SIG:n numero sekä viestialueen  numero  siellä.  Jos
  1135. näitä ei anneta, ne kysytään. Poikkeustapaus on kuitenkin se, että SIG:n  numero
  1136. on määritelty, mutta alueen ei. Silloin siirrytään suoraan alueelle  yksi.  Tämä
  1137. on helppo tapa vaihtaa nopeasti alueryhmää. 
  1138.  
  1139. Joskus verkosta tulee viestejä, joiden merkistö on väärä. Näitä voi  tulla  myös
  1140. käyttäjien replypaketeissa. Viestin voi kuitenkin merkistökonvertoida sen olles-
  1141. sa jo viestikannassa: 
  1142.  
  1143. Command "convert" (N:4) {
  1144.     if(GetUserAccess() < 1000) {
  1145.         rexx("SendModem \(27)[31mNo access\(13)\(10)");
  1146.         rexx("LogEntry Attempted to convert a message");
  1147.         break;
  1148.     }
  1149.     $msg = arg(1);
  1150.     $set = arg(2);
  1151.     if($msg == "") $msg = AskInput(
  1152.         "\(27)[32mConvert which message: \(27)[0m", "", 10, "NUMERIC");
  1153.     if($set == "") $set = AskInput(
  1154.         "\(27)[32mFrom which charset? \(27)[0m", "", 10);
  1155.     if($msg != "") if($set != "") {
  1156.         rexx("SendModem \(27)[32m\(13)\(10)Converting message \(
  1157.             $msg) from \($set) to ISO\(13)\(10)");
  1158.         rexx("LogEntry Converting message \($msg) from \($set) to ISO");
  1159.         rexx("ConvertMsg \($msg) \($set)");
  1160.     } else rexx("SendModem \(27)[31m\(13)\(10)Required argument missing");
  1161. }
  1162.  
  1163. Komento ottaa parametrikseen konvertoitavan viestin numeron sekä merkistön, jos-
  1164. ta konvertoidaan. Yleensä lähdemerkistö on SF7 tai IBM. SF7:ssä  olevassa  vies-
  1165. tissä näkyvät esimerkiksi pienet ä-kirjaimet avautuvina kaarisulkuina ja  pienet
  1166. ö-kirjaimet pystyviivoina. IBM-merkistöllä esimerkiksi ä-kirjaimesta tulee  kur-
  1167. sorin siirto alaspäin - TechnoBBS:llä IBM-merkistön havainnee niin, että skandi-
  1168. merkit puuttuvat kokonaan. 
  1169.  
  1170. BBBS:ssä on myös Duplicate-komento, jolla voidaan jo  kirjoitettu  viesti  ottaa
  1171. uudelleen editoitavaksi. TechnoBBS tarjoaa komennon ReWriteMsg, jota käyttämällä
  1172. vastaavan komennon toteuttaminen siihen on erittäin helppoa: 
  1173.  
  1174. Command "duplicate" (N:3) {
  1175.     $msg = arg(1);
  1176.     if($msg == "") $msg = AskInput(
  1177.         "\(27)[32mDuplicate which message: \(27)[0m", "", 10, "NUMERIC");
  1178.     if($msg != "") {
  1179.         rexx("LogEntry Duplicating message \($msg) as \(GetHighMsg(
  1180.             GetMsgArea()) + 1)");
  1181.         rexx("ReWriteMsg \($msg)");
  1182.     } else rexx(
  1183.         "SendModem \(27)[31m\(13)\(10)Operation cancelled\(13)\(10)");
  1184. }
  1185.  
  1186. Komennolle annetaan parametrinä dupattavan viestin numero.  Viestin  voi  dupata
  1187. vain sen kirjoittaja tai käyttäjä, jolla on viestineditointi-  tai  SysOp-oikeu-
  1188. det. 
  1189.  
  1190. Laitan tähän nyt niitä lupaamiani Extras-valikon ylimääräisiä komentoja.  Komen-
  1191. not ajavat jonkin järjestelmäkomennon ja kertovat käyttäjälle näin  tietoa  sys-
  1192. teemistä. 
  1193.  
  1194. Command "processes" (N:2) {
  1195.     rexx("LogEntry Listing processes");
  1196.     if(upper(arg(1)) == "ALL") dos("PS >T:pr_\(node())");
  1197.         else dos("Status >T:pr_\(node())");
  1198.     rexx("SendASCII T:pr_\(node())");
  1199.     dos("Delete T:pr_\(node())");
  1200. };
  1201.  
  1202. Tämä ohjelma lähettää  käyttäjälle  prosessilistan.  Vaihtoehtoisesti  käytetään
  1203. Status-komentoa  tai  perusteellisempaa  PS-komentoa,  joka  listaa  myös  Exec-
  1204. tehtävät. Tulostus voi olla pitkä,  joten  se  ohjataan  temppitiedostoon,  joka
  1205. lähetetään SendASCII-komennolla. Näin listaukseen saadaan tarpeen vaatiessa  Mo-
  1206. re-prompti. Käytän temppitiedoston nimessä nodenumeroa, koska on  olemassa  mah-
  1207. dollisuus, että komento ajettaisiin yhtä aikaa useammalla kuin yhdellä nodella. 
  1208.  
  1209. Command "memory" (N:3) {
  1210.     rexx("LogEntry Checking available memory");
  1211.     dos("Avail >TECHIO:\(node())");
  1212. };
  1213.  
  1214. Yksinkertainen komento MEM kertoo koneen muistitilanteen. 
  1215.  
  1216. Command "time" (N:3 G:) {
  1217.     rexx("LogEntry Acquiring current time");
  1218.     dos("Date >TECHIO:\(node())");
  1219. };
  1220.  
  1221. Yhtä yksinkertainen TIM-komento kertoo oikean ajan. 
  1222.  
  1223. Command "ds" {
  1224.     rexx("LogEntry Checking disk space");
  1225.     if(upper(arg(1)) == "FULL") dos("Inf >T:ds_\(node())");
  1226.         else dos("Info >T:ds_\(node())");
  1227.     rexx("SendASCII T:ds_\(node())");
  1228.     dos("Delete T:ds_\(node())");
  1229. };
  1230.  
  1231. Komento DS toimii samalla tavalla kuin PR. Se kertoo massamuistien  tilan.  Sitä
  1232. tarkoitusta varten ajetaan Info-komento. Vaihtoehtoisesti  ajetaan  Inf-komento,
  1233. joka kertoo enemmän tietoa kuin vakiokomento Info. Tiedot tulostetaan samaan ta-
  1234. paan kuin prosessilistakin - Inf tulostaa myös muistitilanteen ja Volume-luette-
  1235. lon, joten senkin tuloste voi olla yli sivun mittainen. 
  1236.  
  1237. Tähän mennessä pitäisi jo olla selvää,  että  ihannoin  BBBS:ää!  No,  ei  aivan
  1238. sentään, mutta BBBS:ssä on monia hyviä ominaisuuksia, ja niitä olen tuonut Tech-
  1239. noBBS:ään useita. Yksi näistä on tuki  bulletiineille  eli  "tekstikirjastolle".
  1240. Bulletiinit ovat tekstitiedostoja, joita voi lukea linjalla ja imeä yksitellen: 
  1241.  
  1242. Command "bulletins" (N:4 G:) {
  1243.     rexx("LogEntry Bulletins");
  1244.     $read = arg(1);
  1245.     $bull = "?";
  1246.     while ($bull != "") {
  1247.         if($read == "") $bull = AskInput(
  1248.             "\(27)[32mBulletin number (? for list): \(27)[0m","",40);
  1249.             else {
  1250.                 $bull = $read;
  1251.                 $read = "";
  1252.             }
  1253.         if($bull == "?") $bull = "list";
  1254.         if(str($bull,1,1) == "q") $bull = "";
  1255.             else if($bull == "d") {
  1256.             rexx("ClearFiles");
  1257.             while($bull != "") {
  1258.                 rexx("SendModem \(13)\(10)");
  1259.                 $bull = AskInput(
  1260.                     "\(27)[32mBulletin number to download: \(27)[0m","",40);
  1261.                 if($bull != "") {
  1262.                     rexx("LogEntry Downloading bulletin \($bull)");
  1263.                     rexx("AddFile BBS:Bulletins/\($bull)");
  1264.                 }
  1265.             };
  1266.             $dl = 1;
  1267.             while($dl) {
  1268.                 if(SendFiles()) $dl = 0; else {
  1269.                     rexx("SendModem \(13)\(10)\(13)\(10)\(13)\(10)\(27)[31m
  1270.                         Transfer failed.\(13)\(10)\(13)\(10)");
  1271.                     if(!(GetYesNo("\(27)[36mSend again? \(27)[0m",1,1)))
  1272.                         $dl = 0;
  1273.                 }
  1274.             }
  1275.         } else if($bull != "") {
  1276.             rexx("LogEntry Reading bulletin \($bull)");
  1277.             rexx("SendModem \(12)");
  1278.             rexx("SendASCII BBS:Bulletins/\($bull)");
  1279.             rexx("SendModem \(13)\(10)");
  1280.         };
  1281.     }
  1282. };
  1283.  
  1284. Tämä komento  käyttää  hyväkseen  ClearFiles/AddFile/SendFiles()-toimintosarjaa.
  1285. Olen ajatellut kirjoittaa koodin uusiksi Rexx-kielellä, koska nykyisellään  bul-
  1286. letiineja ei voi konvertoida. Online-Saku-lukijassani olisi valmis konvertointi-
  1287. koodi sekä pakkaus. Ne voisi helposti kopioida  myös  bulletiinitukeen,  jolloin
  1288. myös imettävät bulletiinit voisi konvertoida oikealle  merkistölle  sekä  pakata
  1289. ennen siirtoa.
  1290.  
  1291. Komennolle  voi   antaa   parametriksi   bulletiinin   numeron,   joka   luetaan
  1292. välittömästi, minkä jälkeen tulostetaan prompti. Muuten sen saa  heti.  Paramet-
  1293. riksi voi antaa bulletiinin numeron tai komentoja. Tällä hetkellä  hyväksyttäviä
  1294. komentoja ovat d, jolla voi imeä bulletiineja, tai ?, joka tulostaa  bulletiini-
  1295. listan, sekä q, jolla pääsee takaisin päävalikkoon, mikä onnistuu  myös  enterin
  1296. painamisella tyhjälle riville.
  1297.  
  1298. Bulletiinit pidetään hakemistossa BBS:Bulletins. Bulletiinilista on  tiedostossa
  1299. BBS:Bulletins/list.  Kun  promptiin  vastataan  kysymysmerkillä,  se   muutetaan
  1300. "list":ksi, jolloin lähetetään tuo lista. Käyttäjälle lähetetään suoraan sen ni-
  1301. minen tiedosto, minkä hän on promptiin kirjoittanut. Lisäksi siitä tehdään loki-
  1302. merkintä. Listan tapauksessa tiedoston nimi on "list", joten lokientryksi  tulee
  1303. näppärästi Reading bulletin list... Tässä on esimerkiksi oma bulletiinilistani: 
  1304.  
  1305.        Bulletin List
  1306.  
  1307.     0   Tietoa bulletiineista
  1308.     1   Uploading Instructions
  1309.     2   Star Fleet Top Callers
  1310.     3   Star Fleet Top Message Writers
  1311.     4   Star Fleet Top Uploaders
  1312.     5   Star Fleet Top Downloaders
  1313.  
  1314.        Alaryhmät
  1315.  
  1316.     20  SAKU
  1317.     30  Amigan tulevaisuus
  1318.     40  Amigan laitteisto ja ohjelmisto
  1319.     60  Tee-se-itse
  1320.     80  Sekalaiset
  1321.     100 Star Trek
  1322.  
  1323.     enter to quit bulletins, cls to clear screen, d to download
  1324.  
  1325. Lopussa oleva ylimääräinen ohje on syytä pitää mukana, koska  noita  tietoja  ei
  1326. anneta muualla. Bulletiinit ovat hyvä paikka pitää TopTechin tuottamat  top-lis-
  1327. tat. Niiden generointi lennossa on hidasta ja epäsuotavaa, koska TopTech voi se-
  1328. koittaa TechConin toimintaa. Alaryhmien tekeminen on myös mahdollista. Bulletii-
  1329. nien ei ole pakko olla numeroituja. Niiden nimenä voi olla myös jokin sana, mut-
  1330. ta sanavälejä siinä ei ole suotavaa olla.
  1331.  
  1332. Alaryhmien hakemistot pidetään vastaavissa tiedostoissa, esimerkiksi SAKU-bulle-
  1333. tinien hakemisto on tiedostossa BBS:Bulletins/20: 
  1334.  
  1335.        Bulletin List - SAKU
  1336.  
  1337.     21  Amiga-käyttäjät perustavat rekisteröidyn yhdistyksen
  1338.     22  Uusi HTML-formaatin SAKU-lukija, ideoijana Jukka Aho (30k)
  1339.     23  SAKU-kokous IRC:ssä
  1340.     24  Seuraavan Sakun uusin tilannekatsaus
  1341.     25  Esa Heikkinen perustaa SAKU-netin!
  1342.     26  SAKU-netin nodelista
  1343.  
  1344.     enter to quit bulletins, cls to clear screen, d to download
  1345.  
  1346. Ohje on aina syytä laittaa kaikkiin hakemistoihin. Varsinaiset bulletiinit  ovat
  1347. tietysti tiedostonimiltään BBS:Bulletins/21, BBS:Bulletins/22 ja niin  edelleen.
  1348. Olen nähnyt vastaavan palvelun ainakin Amiga Zonessa, ja se on minun  mielestäni
  1349. erittäin kätevä tapa välittää tekstimuotoista tietoa. 
  1350.  
  1351. Nykyään eräs tärkeimmistä BBS:n palveluista on viestien etäluvun  järjestäminen.
  1352. TechnoBBS tukee QWK:ta ja WWF:ää ja tarjoaa hyvät mahdollisuudet etälukuun, mut-
  1353. ta BA/BW/QU/WU-sarja on aivan liian mahdoton  muistettavaksi.  Näin  ollen  olen
  1354. kirjoittanut kaksi komentoa, grab ja put. Grabilla otetaan  viestit  ja  putilla
  1355. laitetaan vastaukset. Komennot sisältävät alkuperäisten komentojen koodin, mutta
  1356. ne on ympäröity uudenlaisella liittymällä: 
  1357.  
  1358. Command "grab" (G:) {
  1359.     $format = GetUserMisc("OFFLINER");
  1360.     if($format == "") {
  1361.         rexx("SendModem \(13)\(10)\(27)[31mThere is no Offliner defined.
  1362.             \(13)\(10)\(13)\(10)");
  1363.         $resp = GetYesNo("\(27)[0mDo you want to use WWF (or QWK) ", 1, 1);
  1364.         if($resp == 1) $format = "WWF";
  1365.             else $format = "QWK";
  1366.         SetUserMisc("OFFLINER",$format);
  1367.     }
  1368.     if(lower(arg(1)) == "a") SetUserMisc("OFFLINERMODE", 1);
  1369.         else if(lower(arg(1)) == "s") SetUserMisc("OFFLINERMODE", 0);
  1370.     if(GetUserMisc("OFFLINERMODE") == "") {
  1371.         rexx("SendModem \(13)\(10)\(27)
  1372.             [31mYou have no offliner operation mode defined. Using default.
  1373.             \(13)\(10)\(13)\(10)");
  1374.         SetUserMisc("OFFLINERMODE",0);
  1375.         SetUserMisc("WWFASYNC",0);
  1376.         SetUserMisc("QWKASYNC",0);
  1377.     }
  1378.     if($format == "QWK") {
  1379.         rexx("SendModem \(13)\(10)\(27)[33mPacking QWK messages");
  1380.         if(GetUserMisc("OFFLINERMODE")) {
  1381.             rexx("LogEntry Packing QWK messages asynchronously");
  1382.             rexx("SendModem  asynchronously, you will be informed on
  1383.                 completion\(13)\(10).\(13)\(10)\(27)[0m");
  1384.             dos("Run <>NIL: TechQWK \(node()) O BBS:Cfg/TechQWK.Cfg");
  1385.         } else {
  1386.             rexx("LogEntry Packing QWK messages");
  1387.             rexx("SendModem .\(13)\(10)\(13)\(10)");
  1388.             dos("TechQWK \(node()) O BBS:Cfg/TechQWK.Cfg");
  1389.             rexx("SendModem \(13)\(10)");
  1390.             GetHotkey("\(27)[32mPress any key to start sending...\(27)[0m");
  1391.             rexx("SendModem \(13)\(10)\(13)\(10)");
  1392.             rexx("MarkAnyFile \(GetUserPath(GetUserName()))/SFL.QWK");
  1393.             rexx("Download MARKEDONLY");
  1394.         }
  1395.     } else {
  1396.         rexx("SendModem \(13)\(10)\(27)[33mPacking WWF messages");
  1397.         if(GetUserMisc("OFFLINERMODE")) {
  1398.             rexx("LogEntry Packing WWF messages asynchronously");
  1399.             rexx("SendModem  asynchronously, you will be informed on
  1400.                 completion\(13)\(10)\(13)\(10)\(27)[0m");
  1401.             dos("Run <>NIL: TechWWF \(node()) O BBS:Cfg/TechWWF.Cfg");
  1402.         } else {
  1403.             rexx("LogEntry Packing WWF messages");
  1404.             rexx("SendModem .\(13)\(10)\(13)\(10)");
  1405.             dos("TechWWF \(node()) O BBS:Cfg/TechWWF.Cfg");
  1406.             rexx("SendModem \(13)\(10)");
  1407.             GetHotkey("\(27)[32mPress any key to start sending...\(27)[0m");
  1408.             rexx("SendModem \(13)\(10)\(13)\(10)");
  1409.             rexx("MarkAnyFile \(GetUserPath(GetUserName()))/SFL.WWF");
  1410.             rexx("Download MARKEDONLY");
  1411.         }
  1412.     }
  1413. };
  1414.  
  1415. Command "put" (G:) {
  1416.     $format = GetUserMisc("OFFLINER");
  1417.     if($format == "") {
  1418.         rexx("SendModem \(13)\(10)\(27)[31mThere is no Offliner defined.
  1419.             \(13)\(10)\(13)\(10)");
  1420.         $resp = GetYesNo("\(27)[0mAre you using WWF (or QWK) ", 1, 1);
  1421.         if($resp == 1) $format = "WWF";
  1422.             else $format = "QWK";
  1423.         SetUserMisc("OFFLINER",$format);
  1424.     }
  1425.     if(lower(arg(1)) == "a") SetUserMisc("OFFLINERMODE", 1);
  1426.     else if(lower(arg(1)) == "s") SetUserMisc("OFFLINERMODE", 0);
  1427.     if(GetUserMisc("OFFLINERMODE") == "") {
  1428.         rexx("SendModem \(13)\(10)\(27)[31mYou have no offliner operation
  1429.             mode defined. Using default.\(13)\(10)\(13)\(10)");
  1430.         SetUserMisc("OFFLINERMODE",0);
  1431.         SetUserMisc("WWFASYNC",0);
  1432.         SetUserMisc("QWKASYNC",0);
  1433.     }
  1434.     if($format == "QWK") {
  1435.         rexx("SendModem \(13)\(10)\(27)[33mUploading QWK messages.
  1436.             \(13)\(10)\(13)\(10)");
  1437.         rexx("LogEntry Uploading QWK messages");
  1438.         dos("Delete \(GetUserPath(GetUserName()))/SFL.REP quiet");
  1439.         rexx("SendModem \(13)\(10)\(27)[33mStart sending SFL.REP
  1440.             \(13)\(10)\(13)\(10)\(27)[0m");
  1441.         if(ReceiveFiles()) {
  1442.             if(GetUserMisc("OFFLINERMODE")) {
  1443.                 dos("Run >NIL: TechQWK \(node()) I BBS:Cfg/TechQWK.Cfg");
  1444.                 rexx("SendModem \(27)[33mProcessing replies asynchronously
  1445.                     \(13)\(10)\(27)[0m");
  1446.             } else dos("TechQWK \(node()) I BBS:Cfg/TechQWK.Cfg");
  1447.         } else rexx("SendModem \(13)\(10)\(13)\(10)\(27)[31mTransfer failed
  1448.             \(13)\(10)\(27)[0m");
  1449.     } else {
  1450.         rexx("SendModem \(13)\(10)\(27)[33mUploading WWF messages.
  1451.             \(13)\(10)\(13)\(10)");
  1452.         rexx("LogEntry Uploading WWF messages");
  1453.         dos("delete \(GetUserPath(GetUserName()))/SFL.RRF quiet");
  1454.         rexx("\(13)\(10)\(27)[33mStart sending SFL.RRF
  1455.             \(13)\(10)\(13)\(10)\(27)[0m");
  1456.         if(ReceiveFiles()) {
  1457.             if(GetUserMisc("OFFLINERMODE")) {
  1458.                 dos("Run >NIL: TechWWF \(node()) I BBS:Cfg/TechWWF.Cfg");
  1459.                 rexx("SendModem \(27)[33mProcessing replies asynchronously
  1460.                     \(13)\(10)\(27)[0m");
  1461.             } else dos("TechWWF \(node()) I BBS:Cfg/TechWWF.Cfg");
  1462.         } else rexx("SendModem \(13)\(10)\(13)\(10)\(27)[31mTransfer failed
  1463.             \(13)\(10)\(27)[0m");
  1464.     }
  1465. };
  1466.  
  1467. Tämä koodi meni varsin risaiseksi, koska jouduin jakamaan useita rivejä. Jos ha-
  1468. luat hyödyntää sitä, parasta lienee pyytää minulta suoraan  kunnollinen  versio.
  1469. Komennoissa on paljon päällekkäistä koodia, joten tuota voisi parannella  vielä.
  1470. Ideana on, että viestien ottaminen ja laittaminen  onnistuu  samalla  komennolla
  1471. riippumatta siitä, mitä formaattia käyttää.  Käytän  käyttäjämuuttujaa  OFFLINER
  1472. välittämään tiedon siitä, missä formaatissa  viestien  tulee  olla.  Vaihtoehdot
  1473. ovat tuetut QWK ja WWF.
  1474.  
  1475. Toinen käyttämäni muuttuja on OFFLINERMODE.  Se  sisältää  periaatteessa  tiedon
  1476. siitä, pakataanko linjalla vaiko taustalla. Lisäksi sen avulla voidaan aktivoida
  1477. automaattipakkaus loginissa. Lisäksi täytyy kuitenkin asettaa myös  QWKASYNC  ja
  1478. WWFASYNC, jotta bundlerit osaavat toimia oikein.
  1479.  
  1480. OFFLINERMODE voi olla 0, 1, 2 tai 3. Nolla tarkoittaa pakkausta linjalla ja yksi
  1481. taustalla. Myös arvoilla kaksi ja kolme pakataan taustalla. Lisäksi  arvo  kaksi
  1482. aktivoi automaattipakkauksen. Arvo kolme toimii muuten samoin, mutta  asia  var-
  1483. mistetaan vielä käyttäjältä. Näitä toimintoja varten tarvitaan koodia Login.rex-
  1484. xiin: 
  1485.  
  1486.     Mode = GetUserMisc(ln,"OFFLINERMODE")
  1487.     if Mode = 3 then do
  1488.         Mode = Mode - GetYesNo(ln, "Engage bundler? ", 1, 1)
  1489.         SendModem CRLF
  1490.         end
  1491.     if Mode = 2 then do
  1492.         Format = GetUserMisc(ln,"OFFLINER")
  1493.         if Format = "WWF" then address command 'Run >NIL: TechWWF '||ln
  1494.             ||' O BBS:Cfg/TechWWF.Cfg'
  1495.         if Format = "QWK" then address command 'Run >NIL: TechQWK '||ln
  1496.             ||' O BBS:Cfg/TechQWK.Cfg'
  1497.         LogEntry "Autopacking messages using "||Format
  1498.         end
  1499.  
  1500. Tämä koodi tulee Login.rexxin loppuun juuri  ennen  LoginExtra.rexxin  ajamista.
  1501. Koodi ajaa bundlerin, jos OFFLINERMODE on kaksi. Jos se on kolme, asiaa kysytään
  1502. käyttäjältä. Koodi vähentää ovelasti vastauksen moodiarvosta, jolloin siitä  tu-
  1503. lee kaksi, kun käyttäjä vastaa myöntävästi, eli pakkaus käynnistetään! Ja lokiin
  1504. tehdään tietysti merkintä unohtamatta mainita, mitä formaattia käytetään...
  1505.  
  1506. Olen koonnut etälukuvalinnat omaan asetusvalikkoonsa, Offliner  settings.  Koodi
  1507. on tiedostossa BBS:Rexx/OS.rexx. En laita tähän koko koodia, koska sen pituus on
  1508. yhdeksän kilotavua, mutta otan mukaan olennaisimman. Suurin osa  koodista  onkin
  1509. samaa, joka on jo vakiona. Tässä tulee kuitenkin koodi, jolla valitaan  offline-
  1510. rin moodi, paketin formaatti ja merkistökonversio. Mikään näistä  ei  ole  Tech-
  1511. noBBS:n vakiotoiminto. Ensiksi tulee aina asetusohjelman alkuun tuleva asetuksen
  1512. näyttävä koodi ja sitten sen muuttamiseen tarvittava koodi. 
  1513.  
  1514.     des = GetUserMisc(ln, "OFFLINERMODE")
  1515.     select
  1516.         when des = 0 then des = "Online"
  1517.         when des = 1 then des = "Background"
  1518.         when des = 2 then des = "Auto"
  1519.         when des = 3 then des = "Query"
  1520.         otherwise des = "<NONE>"
  1521.         end
  1522.     SendModem CRLF||PURPLE||"Offliner Settings"||CRLF||CRLF||WHITE
  1523.     SendModem "1   "||CYAN||"Packing mode ........................ "||
  1524.         WHITE||des||CRLF
  1525.  
  1526.         when cmdid = "1" then do
  1527.             des = GetUserMisc(ln, "OFFLINERMODE")
  1528.             select
  1529.                 when des = 0 then des = 1
  1530.                 when des = 1 then des = 2
  1531.                 when des = 2 then des = 3
  1532.                 when des = 3 then des = 0
  1533.                 otherwise des = 0
  1534.                 end
  1535.             call SetUserMisc ln, "OFFLINERMODE", des
  1536.             if des = 0 then do
  1537.                 call SetUserMisc ln, "WWFASYNC", 0
  1538.                 call SetUserMisc ln, "QWKASYNC", 0
  1539.                 call SetUserMisc ln, "WWFTASKPRI", 0
  1540.                 call SetUserMisc ln, "QWKTASKPRI", 0
  1541.                 end
  1542.             else do
  1543.                 call SetUserMisc ln, "WWFASYNC", 1
  1544.                 call SetUserMisc ln, "QWKASYNC", 1
  1545.                 call SetUserMisc ln, "WWFTASKPRI", -1
  1546.                 call SetUserMisc ln, "QWKTASKPRI", -1
  1547.                 end
  1548.             end
  1549.  
  1550.     des = GetUserMisc(ln, "OFFLINER")
  1551.     if des = "" then des = "<NONE>"
  1552.     SendModem "2   "||CYAN||"Bundle format ....................... "||
  1553.         WHITE||des||CRLF
  1554.  
  1555.         when cmdid = "2" then do
  1556.             dez = GetUserMisc(ln, "OFFLINER")
  1557.             if dez = "" then dez = "WWF"
  1558.             des = "<NONE>"
  1559.             do while des = "<NONE>"
  1560.                 SendModem CRLF
  1561.                 SendASCII "Text/Offliner.txt"
  1562.                 des = AskInput(ln, "Select: ", "", 2, "NUMERIC");
  1563.                 select
  1564.                     when des = "1" then des = "WWF"
  1565.                     when des = "2" then des = "QWK"
  1566.                     when des = "" then des = dez
  1567.                     otherwise des = "<NONE>"
  1568.                     end
  1569.                 end
  1570.             call SetUserMisc ln, "OFFLINER", des
  1571.             end
  1572.  
  1573.     des = GetUserMisc(ln, "QWKOUTCSET")
  1574.     select
  1575.         when des = "BBS:CharSet/ISO" then des = "ISO"
  1576.         when des = "BBS:CharSet/IBM" then des = "IBM"
  1577.         when des = "BBS:CharSet/SF7" then des = "SF7"
  1578.         when des = "BBS:CharSet/ISOSF7" then des = "ISOSF7"
  1579.         when des = "BBS:CharSet/IBMSF7" then des = "IBMSF7"
  1580.         otherwise des = "<NONE>"
  1581.         end
  1582.     SendModem "8   "||CYAN||"QWK Outbound Character Conversion ... "
  1583.         ||WHITE||des||CRLF
  1584.  
  1585.         when cmdid = "8" then do
  1586.                 CSet = ""
  1587.                 do while CSet = ""
  1588.                     SendASCII "Text/CharSet.txt"
  1589.                     CSet = AskInput(ln, "Select: ", "0", 2, "NUMERIC")
  1590.                     select
  1591.                         when CSet = "0" then CSet = "BBS:CharSet/ISO"
  1592.                         when CSet = "1" then CSet = "BBS:CharSet/IBM"
  1593.                         when CSet = "2" then CSet = "BBS:CharSet/SF7"
  1594.                         when CSet = "3" then CSet = "BBS:CharSet/ISOSF7"
  1595.                         when CSet = "4" then CSet = "BBS:CharSet/IBMSF7"
  1596.                         otherwise CSet = "0"
  1597.                         end
  1598.                     end
  1599.                 call SetUserMisc ln, "QWKOUTCSET", CSet
  1600.                 SendModem CRLF
  1601.                 end
  1602.  
  1603.  
  1604. Tässä vielä esimerkiksi Offliner.txt, joka lähetetään formaattia valittaessa: 
  1605.  
  1606.     The following offliners are available in this system:
  1607.  
  1608.        1 WWF
  1609.        2 QWK
  1610.  
  1611. Select one or press enter for no change or default selection.
  1612.